Posts Tagged oop

Como gerenciar a dependência entre entidades e repositórios?

Engraçado que esse é um assunto onde há sempre mais um “ponto” a acrescentar. Motivados pelo excelente post da Caelum e discussão do GUJ (onde ambos não são tão recentes assim), eu e o Eduardo Amuri discutimos mais alguns pontos… Alguns deles podem já ter sido abordados nos links anteriores, mas vale a revisão.

Eu ia pular a questão inicial, mas vou revisar para que facilite o entendimento do resto do post para quem não leu os links acima. Uma entidade pode conhecer/utilizar um repositório? Acho que a resposta é óbvia: Sim. Repositórios são conceitos de negócios, fazem parte do domínio, e por isso não há nada de errado em uma entidade conhecer um repositório. Não vou entrar no mérito de como o repositório deve ser implementado, não é o meu interesse no momento. Mas vamos lá… Ok, podemos ter entidades que dependem de um repositório, e o problema é: como gerenciar essa dependência?

A seguir, discutimos algumas abordagens para resolver esse problema e chegamos em algumas conclusões, das quais gostaria de compartilhar.

Sugestão I: Receber o repositório pelo construtor
A entidade deve possuir um construtor que receba a dependência.

Vantagens:

  • A dependência fica explícita, ou seja, não há como criar uma entidade sem entregar a dependência já resolvida.

Problemas:

  • Torna o código menos legível, afinal sempre que for instanciar uma entidade, deve-se injetar seu repositório.
    Ex: Pessoa mauricio = new Pessoa ( repositorioDePessoas );
  • Imagine uma entidade que depende de mais de um repositório (por quê não?). Imagine passar os diversos repositórios no construtor. Menos legível ainda…
    Ex: Pessoa mauricio = new Pessoa ( repositorioDePessoas, repositorioDeXPTO, repositorioDeXYZ );
  • Isso fica pior ainda quando definimos um construtor que receba algum atributo, por exemplo.
  • Ex: Pessoa mauricio = new Pessoa ( “mauricio”, “aniche”, repositorioDePessoas, repositorioDeXPTO );

Testabilidade:

  • É fácil injetar um mock no lugar da dependência, para facilitar o teste unitário da entidade.

Sugestão II: Definir um setter para a dependência
A entidade deve ter um método setDependencia() que receba a dependência.

Vantagens:

  • Elimina o problema de resolver a dependência no momento da criação do objeto.

Problemas:

  • Você precisa setar a dependência sempre que for utilizar, e isso pode ser um problema. Você precisará fazer todo o tempo algum tipo de validação no código da entidade para saber se a dependência já foi injetada ou não.
    Ex: public List<XPTO> pegaXpto(int abc) { if(repositorio!=null) repositorio.pegaX(abc); }

Testabilidade:

  • Também é fácil injetar um mock no lugar da dependência.

Sugestão III: Utilizar o próprio repositório para injetar repositórios nas entidades
Conforme sugerido pelo Paulo Silveira e pelo Fabio Kung no post, sempre que um objeto for recuperado pelo seu repositório, o mesmo deve se “auto-injetar” antes de devolver o objeto.

Vantagens:

  • A complexidade do gerenciamento desse dependência fica escondida no código do repositório.
  • O código a ser implementado, de maneira geral, é simples.

Problemas:

  • Apesar de ser simples, deve ser replicado em todos os métodos do repositório. Se o método do repositório retornar uma lista, por exemplo, deve-se repetir o processo em todos os elementos dessa lista.
  • Apenas as entidades que são recuperadas pelo repositório recebem a dependência. Entidades criadas em qualquer outro ponto do projeto não recebem essa dependência de forma automática. Isso pode causar o problema levantado na sugestão II.
  • Caso a entidade mude e venha a depender de um outro repositório, você terá muito código para alterar.

Testabilidade:

  • Da mesma maneira que o repositório injeta a dependência (através de um setter), pode-se injetar um mock.

Sugestão IV: Injetar o repositório por AOP
Uma idéia bastante interessante seria injetar a dependência por AOP. Dessa maneira não importaria como o bean é instanciado, seja ele feito pelo JPA, Hibernate, etc, ou pelo programador. Essa foi uma sugestão levantada pelo Alessandro Lazarotti, nesse post do GUJ.

Vantagens:

  • O código fica bastante claro.

Problemas:

  • A implementação de um aspecto geralmente não é trivial.

Testabilidade:

  • Dependendo do modo que o aspecto for implementado, talvez não seja tão trivial injetar a dependência. Para facilitar, pode-se criar um setter exclusivamente para isso, mas não é muito elegante.

Sugestão V: Resolver a dependência dentro da própria entidade
A idéia seria a própria entidade resolver a sua dependência. No caso, ela mesma invocaria o framework de DI, e recuperaria a instância da dependência. Pode-se até pensar em uma Factory para que a entidade não dependesse diretamente de uma implementação de framework de DI.

Vantagens:

  • Solução bastante simples.
  • Acaba com o problema da necessidade da entidade validar se a dependência foi injetada ou não, já que ela mesma vai resolver.

Problemas:

  • Não é muito elegante fazer com que a própria classe resolva suas dependências.

Testabilidade:

  • Como é a própria classe que resolve a dependência, você precisará de um setter para conseguir injetar um mock. Como dito acima, não é muito legal.

(*) Além do que foi citado acima, existe um outro problema, ortogonal ao das dependências, e diz respeito diretamente à implementação desse projeto em particular. Por estarmos discutindo uma aplicação web, o contexto do Spring está guardado dentro do contexto web. Para que um POJO qualquer recupere esse contexto, ele precisaria conhecer o servletContext. Ainda não encontrei uma maneira muito clara para resolver isso, mas minha sugestão é criar uma classe singleton que conteria um atributo com uma instância de contexto do Spring, e essa instância seria injetada por um filter ou um listener qualquer, que subiria logo após ao listener do Spring. A partir daí, todas as classes de domínio fariam acesso à esse singleton para capturar o contexto.

Portanto, é possível gerenciar essa dependência de diversas formas, todas elas com suas vantagens e desvantagens. E você, como faz?

, ,

4 Comments

PRE – Princípio da Responsabilidade Exclusiva

Segundo o livro Use a Cabeça: Desenvolvimento de Softwares, a definição de PRE é: Cada objeto de seu sistema deve ter uma responsabilidade exclusiva e todos os serviços do objeto devem estar orientados à execução dessa responsabilidade. Em outras palavras, uma classe deve ser responsável por uma determinada função, e só ela pode exercê-la.

Uma classe Carro, por exemplo, deve ser unica e exclusivamente responsável por realizar funções de um carro (como medir óleo, acender farol, etc). A classe Carro não deve ser responsável por saber se lavar (essa é uma responsabilidade que poderia ser entregue a uma classe LavaRapido, por exemplo).

Conversando hoje com o Murilo Amêndola, discutimos sobre como saber quais as responsabilidades de uma classe. Concordamos que não é uma tarefa trivial e que depende de muita experiência para que todas suas classes tenham um alto nível de coesão. Lendo hoje um trecho do livro citado acima, encontrei um algoritmo muito interessante para saber se determinada responsabilidade deve ou não pertencer a determinada classe. Vou reproduzí-lo aqui:

  1. Escreva, em uma folha de papel, várias linhascomo essa: O [espaço] [espaço] sozinho. Escreva 1 linha dessa para cada método da classe que você está testando quanto ao PRE.
  2. No primeiro espaço em branco de cada linha, preencha o nome da classe. No segundo espaço em branco, anote um dos métodos da classe. Faça isso para todos os métodos.
  3. Leia cada linha em voz alta. Você pode até adicionar uma letra ou palavra para que a leitura fique adequada. Se a frase que você disse fez algum sentido, então esse método realmente pertence a essa classe. Se não fizer nenhum sentido, então provavalmente esse método não pertence a essa classe.

Voltando ao exemplo da classe Carro:

  • O carro se liga sozinho. (CERTO)
  • O carro se desliga sozinho. (CERTO)
  • O carro trocaPneus sozinho. (ERRADO)
  • O carro dirige sozinho. (ERRADO)
  • O carro se lava sozinho. (ERRADO)
  • O carro medeOleo sozinho. (CERTO)

Repare que faz todo sentido o carro medir óleo sozinho, enquanto não faz sentido nenhum o carro se dirigir sozinho (esse método deveria estar em uma classe Motorista), ou se lavar sozinho (deveria ser responsabilidade da classe LavaRapido)!

O método liga e desliga não fazem taaanto sentido (afinal, um carro não se liga sozinho), mas nesse caso, não vejo outra alternativa (é o carro que sabe como se ligar!). Isso mostra que a regra acima é apenas uma diretriz, e por isso você deve usar de bom senso e de sua experiência.

O interessante é que você pode adaptar essa regra até para métodos que recebem parâmetros. Se você tiver um método troca(peça), você escreve a seguinte sentença: O carro troca [uma]  peça sozinho. No caso, também não faz muito sentido, você poderia ter uma classe Mecânico, com o método troca(peça, carro).

Enfim, é uma análise interesse, não acham?

(Exemplo retirado do livro Use a Cabeça: Desenvolvimento de Softwares, capítulo 5.)

, ,

6 Comments