Fluent Interface

Fluent Interface é usada para deixar o código muito mais limpo, fácil de entender e dar manutenção. A idéia é que o código do domínio fique mais parecido com a forma que falamos, para um melhor entendimento do código. O objetivo é fazer uso de uma DSL Interna para se aproximar à linguagem natural, no nosso caso o Português!

Vou mostrar o exemplo de um código e refatorá-lo até que fique fluente. Trata-se de uma aplicação de transação bancária que será demonstrada em três fases.

Fase 1:

Imagem19

Quais são os problemas desse código? Vários! Ele funciona normalmente e vai trazer os resultados esperados, porém ele pode se tornar muito ruim para dar manutenção por não ser fluente. Vamos ver por quê?

A entidade Conta parece não ter inteligência nenhuma, pois ela não sabe debitar ou creditar um valor em seu saldo. Outro problema é que se o calculo do débito ou do crédito mudar, será necessário alterar todo lugar na aplicação que estiver usando esse cálculo pois ele não está centralizado na classe Conta. Esse código precisa de comentários pois ele não é auto-explicativo.

Um objeto deve, em termos de Orientação a Objetos, saber o porquê de sua existência e conhecer seus estados válidos. O objeto não deve fornecer métodos que invalidem seu estado. Nesse exemplo, uma classe cliente poderia esquecer um parênteses de deixar o saldo inválido:

conta.setSaldo(conta.getSaldo() + valor * 2 );

Não devemos pensar em uma classe como uma mera coleção de atributos. Objetos também não são atributos mais funções, e sim estado + comportamento. O Phillip Calçado explica muito bem isso!

Refatorando, o método ficaria assim:

Fase 2:

Imagem20

Um pouco melhor pois agora não precisa de comentários, já que a Conta sabe debitar e creditar. Porém esse código ainda tem muito ruído e pode se tornar mais legível.

Vou dar um exemplo de como ficaria a implementação do mesmo método usando fluent interface.
Fase 3:

Imagem21

O código fica bem mais limpo. A implementação dos outros métodos ficaria assim:

Imagem22

Nesse último exemplo utilizei o conceito de Repositório, o qual faz mais sentido no domínio do que os DAOs. A validação do valor do dinheiro no primeiro exemplo agora fica no construtor na classe dinheiro, o que faz mais sentido.

Imagem23

O Guilherme explicou muito bem no blog dele sobre o assunto nesses posts. Vale a pena dar uma espiadinha.🙂
Publicado em design. Tags: , . 11 Comments »

11 Respostas to “Fluent Interface”

  1. Fabio Queiroz Says:

    Tudo é uma questão de ponto de vista.

    O código fluente
    List clientesAtivos = cadastro.clientes().status().ativo().lista()

    poderia ser
    List clientesAtivos = dao.getClientesAtivos()

    Como recomenda-se que existam comentários Javadoc em implementações (não que todos os programadores façam isso), o descritivo seria suficiente para qualquer programador. O retorno do método é auto-suficiente, não necessitaria de um nome explícito “lista()”.

  2. Ricardo Almeida Says:

    Correto Fábio!

    O javadoc é importante sim, porém mais importante é o nome do método, pois o javadoc pode facilmente ficar desatualizado. Se um código está mal escrito, ou pode ser otimizado, devemos refatorá-lo para facilitar a manutenção e deixá-lo mais legível, e o javadoc poderá ficar desatualizado.

    Mesmo que o javadoc esteja integro, o entendimento do código não será tão rápido se o programador tiver que ler o javadoc ao invés do próprio código.

    A fluent interface também facilita a conversa com o cliente, pois essa conversa será algo bem parecido com o que está no código. No domínio da aplicação não é bom ter DAOs misturados com a regra de negócio pois DAO não existe no domínio do cliente. Dao é uma infraestrutura que pode ser abstraída do domínio.

    Já sobre o método lista(), isso não é regra e cada um pode implementar da maneira que achar fluente.
    Eu decidi criar esse método para que a ordem dos métodos não importasse. É até parecido com a forma que o Criteria do Hibernate trabalha. Outro bom exemplo de fluent interface é o Criteria.

  3. Felipe Says:

    Ricardo,

    Sem ter me aprofundado muito na questão, tenho algumas dúvidas:

    1) Eu teria de criar um repositorio para cada atributo de um objeto?
    2) Me parece um sintaxe muito complexa ainda. Imagine que outro desenvolvedor tenha de consumir o ClienteDAO. Ele terá de conhecer o comportamento de cada metodo a ser chamado. Se isso fosse distribuído como uma Lib para outro projeto, sem os fontes, isso seria um problema. Não me parece que os nomes dos métodos estejam revelando sua inteção nesses casos. Por exemplo: cadastro.clientes().status().ativo().

    Me parece um problema na concepção do atributo status. Por que não fazer cadastro.clientes().status(“ATIVO”) ou usar uma Enum pra isso?

    Sei que essas questões que levantei existem mais por restrição da linguagem do que por questões de modelagem, mas seria interessante continuar o estudo desse caso.

    Grande Abraço,

    Felipe

  4. manifestonaweb Says:

    Oi Felipe,

    Muito obrigado por me lembrar de falar sobre o enum. O enum estaria dentro da classe Status.

    Sobre os repositórios, não precisa criar um pra cada atributo. No exemplo eu criei um pra cliente e um para status apenas porque eu queria essa fluencia.

    O cliente não precisará conhecer a implementação do ClienteDAO justamente porque os métodos são auto-explicativos. O Criteria do hibernate é um exemplo de fluent interface, e você precisa conhecer a implementação do Criteria? O Hibernate é distribuido em jar e isso não é problema!

    Você tem uma sugestão para melhorar a sintaxe que você acha ainda complexa?

  5. Felipe Says:

    Bom, na verdade o problema na minha opinião está na modelagem do exemplo. Concordo que não é problema utlizar Fluent Interface em uma lib. A questão é se os metodos estão aderentes à premissa de revelar suas intenções. Mas ainda não estou preparado evoluir o seu exemplo. Como falei, talvez seja por restrições de tecnologia. Mas vou analisar melhor seu código pra te passar uma resposta mais completa da minha opinião.

    Grande Abraço,

    Felipe

  6. manifestonaweb Says:

    Felipe,

    Sobre a modelagem, nesse exemplo eu poderia simplesmente substituir a classe Status por um enum, só que assim eu não conseguiria dar um exemplo mais completo da fluent interface. Imagine então que o status seja mais complexo que uma simples string “Ativo” ou “DESATIVADO”, mas que tenha toda uma lógica dentro da classe Status. Por esse motivo eu criei a classe status.

  7. Felipe Regalgo Says:

    E ae Ricardo!!

    Bom… Você falou dos benefícios da fluent interface e tal… mas acho bom tb citar alguns pontos negativos né…
    Vc não acha que na maioria dos casos uma fluent interface traz uma complexidade desnecessária para a criação do código?
    Como por exemplo fazer:
    cadastro.clientes().status().ativo().lista()
    Na ideia fico bonitinho e tal, mas na pratica dá um puta trabalho chato implementar essa estrutura ai!!!

    Sendo que AS VEZES poderiamos muito bem fazer:
    cadastro.obterTodosOsClientesComEstatusAtivo();
    também ficaria intuitivo e mais rápido de implementar.

    ao invés de ter todo um trabalho para implemetar uma estrutura de encadeamento com vários ‘pontos’ como seria o caso de ‘cadastro.clientes().status().ativo().lista()’ faria da forma mais simples.
    Acho que a idéia de usar fluent interface é muito legal e em alguns casos muito útil, como foi no exemplo do Guilherme Chapiewski. Porém tenho receio do pessoal achar que fluent interface é a ultima moda e fazer o código inteiro usando isso, e adicionando muita complexidade desnecessária no desenvolvimento, caracterizando assim um Anti-Pattern!!!
    Talvez seria interessante mostrar quando usar e quando não usar. Acho que um bom momento pra se usar uma fluent interface seria para desenvolver uma biblioteca para outros utilizarem (WebServices) por exemplo. Ai vc tornaria muito simples e flexível de ser utilizado. Agora creio que se for pra vc mesmo utilizar é muito mais rápido e pratico vc utilizar o código tradicional, com nome do método gigante q informa o que o método faz, não necessitando de javadoc e pronto e acabou. Não me lembro de ter precisado utilizar fluent interface… vc usa normalmente?? Com que freqüência?
    Bom, só uma correção:

    Se vc lê:
    1- cliente get Status igual status ativo
    cliente.getStatus().equals(Status.ATIVO))

    2 – cliente é status ativo
    cliente.isStatusAtivo()

    3 – Status do cliente é ativo
    cliente.status().isAtivo()

    Não!!!!! Usando a sua lógica o terceiro item se lê: “cliente status é ativo” o que não é muito intuitivo. Se vc quer o efeito “Status do cliente é ativo” na minha opinião poderia fazer:
    status.doCliente(cliente).ehAtivo(); //ai sim vc esta lendo da forma que fala

    Outro exemplo:

    cadastro.clientes().status().ativo().lista()…. eu leio “cadastro cliente status ativo lista” e não “listar clientes do cadastro com status ativo” como vc disse. Para ler deste jeito acredito q deveria ser feito da seguinte maneira

    listar().clientesDoCadastro(cadastro).comStatus().ativo(); // quero ver o trabalho pra implementar isso!!!!! ehehhe

    ou seja o nome dos métodos EXATAMENTE da forma que se fala.. ai sim pra mim está fluente. O que acha?

    Falow ai kara!!!! E parabéns pelo blog!

  8. Ricardo Almeida Says:

    Felipe,
    No exemplo que dei, apesar de didático, não teve muita complexidade para implementar. Só precisei trabalhar com interfaces e repositórios. Mas o que você falou é verdade, se tiver complexidade para implementar é melhor fazer da maneira convencional. Porém tem um ótimo livro sobre DDD do Evans que pode te ajudar na hora de implementar. A fluent interface não deve ser usada no sistema inteiro, apenas no model da sua aplicação. O livro separa bem isso, pois temos três pacotes: modelo, infraestrutura e aplicação. A fluent interface está no modelo e deve ser usada caso for ajudar na fluência do código. O risco de ser usado erroneamente existe, é por isso que deve-se ler o livro!
    Ainda não implementei fluent interface nos projetos pois nenhum deles foi arquitetado seguindo DDD, porém o conceito de código intuitivo eu uso. Para mim esse ainda é um caso de estudo e por isso estou expondo minhas idéias para discusão. Assim o conhecimento sobre esse assunto será bom para quando houver a necessidade de implementar.
    A fluência poderia ser melhorada facilmente se desconsiderarmos as restrições da linguagem. Eu implementei e senti as restrições na pele e o resultado foi usado como exemplo🙂 Para melhorar completamente a fluencia, uma alternativa seria construir uma DSL, mas tem a complexidade que você disse.
    A idéia não foi escrever o código como se estivesse lendo um texto, e sim da maneira que eu entendo quando leio o código. Porém pode ser que uma outra pessoa tenha uma leitura diferente sim!
    Thanks!

  9. Alessandro Lazarotti Says:

    Olá Ricardo, tudo bom?
    Embora não seja trivial de se implementar, é usual, e com ganho, implementar Fluent Interface diretamente em QueryObjects. Por exemplo:
    //query object
    Busca busca = QueryObjectFactory.create();

    List clientesAtivos = repositorioDeClientes
    .executa(busca.por(atributo
    .doTipo(“status”),
    igualAh(ATIVO)));

    É importante a sacada de deixar o nome de variaveis “Syntactic Sugar”, como “busca”, pois somente FluentAPI muitas vezes não resolve.
    Considerações: atributo. e ATIVO utilizam de importação estática😉

  10. Ricardo Almeida Says:

    Oi Alessandro!
    Interessante a idéia de QueryObjects.

    Vou publicar um novo post mostrando uma fluência bem maior que esse post, utilizando de técnicas de Groovy e clousures.

    Abraços,

  11. Criando uma DSL « Manifesto na Web! Says:

    […] Recentes Ricardo Almeida em Fluent InterfaceAlessandro Lazarotti em Fluent InterfaceNicholas em Escalabilidade […]


Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: