DEV Community

Cover image for Utilizando boas práticas da Injeção de Dependência com uso do Spring
Joângele Lopes da Silva
Joângele Lopes da Silva

Posted on

Utilizando boas práticas da Injeção de Dependência com uso do Spring

A principal vantagem de se utilizar este Design Partterns (padrão de projetos) no desenvolvimento de aplicações, é ele provê a Injeção de dependência (DI), que nada mais de que uma das maneiras de se fazer inversão de dependência ou de controle (IoC), que facilitará o desenvolvimento, tornando a codificação mais compreensível, permitindo bom ganho de produtividade, desacoplamento entre classes, facilitando os testes.
O termo dependência em programação orientada a Objetos significa, de modo geral, quando uma classe depende de uma outra classe para prover suas funcionalidades e/ou indica que um objeto depende da especificação de outro objeto.
Injetar é quando passamos uma classe para outra classe como parâmetro que irá consumi-la, ou seja, utilizar-se de suas propriedades e métodos para poder funcionar de forma mais eficiente e desacoplada possível, tornando um contexto mais fácil de dar manutenção e melhorando a escalabilidade do sistema como um todo.

Problema Exposto

Segue um exemplo de dependência e alto acoplamento entre classes ProcessaFolhaMensal e ProcessaFolhaServidorService, são exemplos de classes simples, as quais as serem instanciadas e executadas terão o intuito de simular um processamento da folha de pagamento para um determinado servidor, para servir como exemplo e base inicial ao tema em questão:

image

A classe ProcessaFolhaMensal é responsável por receber o servidor, paramentos: mês e ano, realizar o início do processamento mensal de folha para o respectivo servidor, o qual é dado como conclusivo após a impressão na tela da mensagem simulada.

image

Já a classe ProcessaFolhaServidorService está sendo responsável por instanciar a classe ProcessaFolhaMensal e executar seu método processaFolha, que é o processamento principal, a classe também recebe os parâmetros que serão repassados, imprime na tela o texto de LOG de processamento, realizando as simulações necessárias para garantir a consistência do processo.

Imagine se tivéssemos um projeto com 100 ou mais classes implementadas, todas elas contendo instanciações da classe ProcessaFolhaMensal, e em seguida tivéssemos a necessidade de adicionar mais parâmetros nela, isso inviabilizaria o funcionamento de todas as outras classes dependentes dela, onde neste caso teríamos que realizar alterações no ponto de instanciação de todas as classes que dependem de ProcessaFolhaMensal, acarretando em um tempo maior na alteração das classes, como também as alterações idênticas nos diversos pontos de instanciação de cada classe .
A solução para o problema da classe ProcessaFolhaServidorService é: criar um construtor passando a instancia da classe processaFolha, desse modo evitaríamos dependência entre ambas de forma expressa, isso explica claramente o porquê do termo “injeção de dependência”, é que ao invés da classe de mais alto nível instanciar internamente a classe de baixo nível, nesse caso solucionamos passando ela como parâmetro através do seu construtor. Segue abaixo o exemplo da classe refatorada:

image

Pois bem, agora estamos realmente injetando dependências, neste caso para que a classe ProcessaServidorService funcione, não é necessário instanciar dentro do seu contexto a classe ProcessaFolhaMensal basta apenas passá-la como parâmetro.
Neste caso solucionamos o primeiro problema, mas o segundo ainda não, onde ele afere o princípio da inversão de dependência, que é um dos cinco princípios do SOLID, Criados por Robert C. Martin para facilitar o trabalho dos programadores, cuja principal finalidade é a diminuição do acoplamento entre as classes, e define o significado de que uma classe deverá depender abstração e não de implementação de outra, ou seja: classes de alto nível não deve depender de classes de baixo nível. Uma solução para esse caso seria criar uma interface entre elas onde neste caso tanto a de baixo nível quanto a de alto nível não dependeriam uma da outra e sim da interface.
Digamos que, ao invés de processar a folha mensal, haja a necessidade de processar a folha natalina, folha suplementar, etc. Onde no contexto das novas classes teria também o método processar que é uma forma de polimorfismo, onde vários objetos diferentes tem o mesmo método que podem ser implementados de maneiras diferente. A interface também é uma opção, visto que atende perfeitamente a essa necessidade, segue o diagrama abaixo:

image

Implementação do código:

image

image

image

image

Teste de execução do código:

image

Resultado da execução após compilação:

image

O exemplo acima passa a instância da classe ProcessaFolhaMensal nas linhas 25 e 26, como parâmetro para a classe de nível mais abaixo, executando o método processar, a qual implementa da interface ProcessaFolha, que irá executar a codificação a ela atribuída através do método processar, mostrando o comportamento implementado para esse método principal polimórfico. Se quisermos que no exemplo acima em vez de folha mensal fosse processado a folha natalina bastaria alterar a linha 36, ficando da seguinte forma:

image

A única mudança é que substituímos a instanciação da classe ProcessaFolhaMensal para ProcessaFolhaNatalina.

Utilizando o Spring

O Spring é o framework mais usado pelos desenvolvedores na atualidade, no que diz respeito ao uso do padrão “Inversão de controle”, possui e utiliza o BeanFactory que é como um fabricante genérico e gerente ao mesmo tempo, responsável pela relação entre os objetos, onde utiliza-se de uma estrutura bem definida para recuperação destes pelo nome. O Spring possui uma estrutura onde não necessitamos instancia-los, essas instancias podem ser feitas de forma automática, para esta finalidade, necessitamos anotar quais deles poderão ser instanciados, gerenciados, e controlados automaticamente, os quais são chamados de BEAN, que são objetos instanciados por um container Spring IOC, criados utilizando metadados que contem configurações fornecidas pelos desenvolvedores ao contêiner.

A Injeção de dependência no Spring poderá ser feita por meio de três pontos de Injeção:
*Por propriedade ou injeção de campo através da anotação @Autowired, que definirá o ponto exato onde a injeção de dependência irá ocorrer:

image

@Service -> marcará os BEANS indicando que é classe contendo regras de logica de negócio. Esta anotação poderia ser substituída por @Component que serve para marcar os BEANS como componente gerenciados do Spring, com a diferença que por convenção, sempre que se tratar de classes de camadas de serviços é interessante utilizar a primeira, visto que indicaremos ao Spring que esta faz parte da camada especifica de negócio. Resumindo @componente é um caso especial de @Service assim como @Repository, que tem a mesma finalidade, só que permite lançar algumas exceções específicas de persistência.

*Por construtor -> neste caso criamos um construtor para classe ProcessaFolhaService que possui uma anotação @Service indicando que esta é um Bean gerenciado pelo Spring, esta necessita da instância da classe ProcessaFolha, pois bem poderemos implementar da seguinte forma:

image

Poderíamos implementar este exemplo também de outra maneira, apenas anotando a classe ProcessaFolhaMensal também como componente Bean gerenciado pelo Spring, segue:

image

A anotação @Component foi adicionada na linha 1 e @Autowired foi comentada na linha 23, neste caso o Spring entenderá que a classe ProcessaFolhaMensal é definida como Bean, que poderá ser localizada, instanciada, configurada e injetada, sendo feito de forma transparente e automática, não havendo a necessidade de instanciar a classe ProcessaFolha contida na classe ProcessaFolhaService.

Se precisarmos implementar mais uma classe da interface ProcessaFolha e anotarmos como componente Spring utilizando @Component, @Service ou outro similar, precisaremos anotar qual das classes deve ser instanciada automaticamente, neste caso teremos duas classes implementando uma mesma interface e o Spring não saberá qual delas deverá instanciar primeiro e lançará uma exceção, pois existe um problema de ambiguidade. Para resolver este problema basta anotarmos a classe que queremos que o Spring instancie primeiro, utilizando a anotação @primary, conforme implementação abaixo:

image

A anotação @primary foi adicionada na linha 1 do código acima, para informar ao Spring que a instanciação automática da classe ProcessaFolhaMensal terá prioridade em relação a ProcessaFolhaNatalina, onde nesse caso será solucionado o problema de ambiguidade.

*Por Set (método), neste caso podemos adicionar um método Set e anotá-lo. Segue a implementação abaixo:

image

@Configuration -> é uma outra anotação do Spring que declara também para ele que a classe anotada fara parte do rol objeto gerenciados, com a diferença de que podemos passar configurações durante o tempo execução, no ato de instanciação do objeto pelo Spring, neste caso recomenda-se criar uma classe separada de configurações, para classes que serão anotadas. Um bom exemplo prático para o nosso caso, seria que no ato da instanciação automática da classe ProcessaFolhaMensal2, um método que retornasse ou não a margem consignável do servidor através da modificação de valores de uma propriedade, ou seja, que pudesse ter as opções de exibir ou não a margem consignável do servidor após o processamento. Segue o exemplo do uso e da implementação abaixo:

image

Comentamos a anotação @Component, pois não é necessária já que estamos usando a outra anotação @Configuration na classe FolhaConfiguration, que instância e seta o valor de habilitação de exibição do valor da margem consignável. A anotação @bean utilizada na linha 4 indicará ao Spring que o objeto anotado será instanciado e criado para deixar disponível para injeção em outras classes, no caso o ProcessaFolhaMensal2.

Conclusão:
Notamos, através de exemplos explanados neste artigo, a importância do entendimento e da adoção das boas práticas de uso da injeção de Dependência e inversão de controle no dia a dia da POO, através de exemplos práticos, utilizando também Spring Framework que através dos Beans gerenciáveis, facilitam os trabalhos, diminuindo o tempo de desenvolvimento, deixando as classes, quando necessário, menos desacopladas, simplificando assim os testes de código.

Nota: Este artigo faz parte de um desafio aceito no curso de Especialista REST, ministrado pela equipe da Algaworks com a finalidade de aprimorar e colocar em pratica os conhecimentos dos alunos matriculados.

Top comments (0)