Como funciona a famosa injeção de dependência e porque ela é importante
Tags: braziliandevs, beginners, java, tutorial
Published: 2021-09-30 11:41:33



Um ponto interessante sobre o mundo **Java** é a enorme quantidade de atividades em construir alternativas para o [J2EE](https://www.guj.com.br/t/voce-sabe-o-que-e-j2ee/204071/3).

Muito disso vem da complexidade do universo do **J2EE**, mas além disso, explorando alternativas é de onde vem as ideias criativas.

Um problema comum em lidar com diferentes elementos é, por exemplo: "Como conectar uma arquitetura web com a interface do banco dedados quando eles são construídos por diferentes times e com conhecimentos distintos?"

Foi desse tipo de problema que surgiram inúmeros frameworks com o objetivo de conectar os componentes do J2EE de diferentes camadas. No caso do Java, o mais conhecido é o [Spring](https://spring.io/).

Inclusive, até o **Spring** já conta com alguns "variantes" digamos assim por conta de saber trabalhar muitos bem com essa componentização. Frameworks que se basearam/propõem a serem melhores que o Spring, por exemplo, são: [Micronaut](https://micronaut.io/) e [Quarkus](https://quarkus.io/).

Por baixo desses frameworks temos um número interessantes de design principles, o que vai além dos containers do J2EE. Vamos ver alguns desses princípios!

##Componentes e Serviços

Para fins de padronização de termos, aqui utilizaremos componentes no sentido de: arquivos .JAR, dll ou até mesmo a importação de uma biblioteca.

E serviços no sentido de: serviço web, mensageria, RPC ou até mesmo um socket.

##Exemplo simples

Vamos utilizar o exemplo de um componente que lista filmes por diretor.

```
class ConsultorDeFilmes {

    public Filme[] filmeDirigidoBy(String diretor) {
        List todosFilmes = finder.findAll();
        for (Iterator it = todosFilmes.iterator(); it.hasNext(); ) {
            Filme filme = (Filme) it.next();
            if (!filme.getDiretor().equals(diretor)) it.remove();
        }
        return (Filme[]) todosFilmes.toArray(new Filme[todosFilmes.size()]);
    }
}
```
A implementação dessa função é totalmente simples, é pedido para encontrar um objeto retornar todos os filmes da lista. Depois é solicitado que ele retorne somente os filmes dirigidos pelo diretor que queremos.

O ponto que vamos focar nessa implantação é no finder.findAll();

mais especificamente em como nós conectamos o objeto que é solicitado (o diretor) com um objeto finder em particular. A razão disso ser interessante é que eu quero que o método filmeDirigidoBy seja completamente independente de como todos os são armazenados.

Basicamente o método que estamos estudando se resume ao finder e tudo o que o finder faz é saber como responder ao método findAll. Dessa forma, podemos definir uma interface para o finder.

```
public interface ProcuraFilmes{
	List findAll();
}
```

Com isso, nós desacoplamos o findAll(), mas ainda assim teremos que torná-lo em classe concreta em algum momento para retornar os filmes que queremos. Então faremos de forma que o código passe para o construtor do ConsultorDeFilmes:

```
class ConsultorDeFilmes {

		private ProcuraFilmes finder;
		public ConsultorDeFilmes(){
			finder = new ColunaDelimitadaProcuraFilmes("filmes1.txt");	
	}

}
```

O nome da classe de implementação vendo do fato de estamos usando um arquivo .txt delimitado por colunas.

Agora se eu usar essa classe somente nessa implementação, está ok, mas o que acontece se mais alguém quiser uma cópia dessa mesma aplicação? 🤔

Simples, caso a pessoa armazene os filmes em um arquivo .txt delimitado por colunas chamado "filmes1.txt", a aplicação vai rodar tranquilamente. Caso use um nome de arquivo diferente, por exemplo, ela só precisa alterar o nome na classe de implantação, até aqui, ok.

Porém e se ela utilizar uma forma totalmente diferente de armazenar os filmes, por exemplo, num banco de dados SQL, um arquivo XML, um serviço web, entre outros?🤔🤔

Nesse caso, precisaremos de um classe diferente para fazer a implantação e puxar os dados. E aqui está o ponto: Porque nós colocamos o ProcuraFilme como uma interface, isso não vai alterar o método filmesDirigidoBy , mas ainda assim precisamos de uma forma de instanciar o finder de forma correta na implementação.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v1rwl3a0cm898iuvuiue.png)


MovieLister == ConsultorDeFilmes // MovieFinder == ProcuraFilmes // MovieFinderImpl == implementação concreta da interface. Fonte: https://martinfowler.com/articles/injection/naive.gif

A imagem acima mostra como a nossa classe ConsultorDeFilmes está dependendo tanto da interface ProcuraFilmes como de sua classe concreta, nós precisamos que a classe dependa apenas da interface.

Mas como fazer isso? 🤔🤔🤔

A classe de implementação não está linkada à aplicação em tempo de compilação e não sabemos como essa pessoa fará a implementação da interface. Ou seja, corremos o risco de ter o código alterado (implementação da interface que consta no ConsultorDeFilmes) e, consequentemente, a aplicação vai quebrar.

Por isso precisamos que a classe ConsultorDeFilmes dependa apenas da interface, ignore a forma como ela é implementada e seja capaz de rodar a instância dela.

> Nós chamamos essa situação de **plugin**, numa situação real podemos ter milhares de serviços e componentes que precisamos implementar de diferentes formas. O plugin interage com esses serviços e componentes através de interfaces e, então, eles decidem as implementações sem alterar a nossa aplicação.

Voltando a nossa pergunta anterior: Mas como fazer isso? 🤔🤔🤔

A resposta: **Inversão de Controle**

##Inversão de controle

> Como mencionado anteriormente, a inversão de controle é uma característica muito comum de alguns frameworks, mas falar que eles são especiais por conta disso é o mesmo que falar que uma ferrari é especial porque ela tem pneus.

O que importa mesmo é: "Qual aspecto de controle esses frameworks invertem?"

Inversão de controle é o que diferencia um framework de uma biblioteca. Uma biblioteca é somente um conjunto de funções que você utiliza, já um framework é um design abstrato com comportamentos. No framework você precisa colocar comportamento em vários lugares em subclasses ou plugando em suas próprias classes f(o Spring faz isso utilizando Beans e Annotations, por exemplo).

Na nossa aplicação, instanciamos o finder diretamente, o que o impede de ser um plugin. Uma saída é garantir que qualquer usuário desse plugin siga as convenções que queremos e injete isso em suas implementações.

Pensando nisso, Inversão de Controle (Inversion of Control - IoC) é um termo muito genérico e, de acordo com vários especialistas, o termo, nesse caso, seria **Injeção de Dependência** (Dependecy Injection).

##Formas de Injeção de Dependência

A ideia da injeção de dependência é ter um "montador" ou assembler que poupla o campo na classe ConsultorDeFilmes com a implementação apropriada da interface ProcuraFilmes, ficando da seguinte forma:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/agg2udilk5b64b0na9y0.png)

MovieLister == ConsultorDeFilmes // MovieFinder == ProcuraFilmes // MovieFinderImpl == implementação concreta da interface. Assembler == Montador Fonte: https://martinfowler.com/articles/injection/injector.gif

Em cima desse diagrama, temos 3 formas principais de fazer a injeção de dependência:

* Inversão de Controle tipo 01 - **Injeção por interface** (Injection Interface)
* Inversão de Controle tipo 02 - **Injeção por setter**(Setter Interface)
* Inversão de Controle tipo 03 - **Injeção por contrutor**(Constructor Interface)

Para o nosso caso, vamos falar sobre o Spring.

Essa framework inclui camadas de abstração para transações, frameworks de persistência em banco de dados, aplicação web e JDBC. Ele suporta tanto a injeção por construtor como por setter, mas os desenvolvedores dele preferem a injeção por setter, vamos ver como fica nesse caso.

Para que o nosso ConsultorDeFilmes aceite a injeção, temos que definir o método set para o serviço.

```
class ConsultorDeFilmes {

	private ProcuraFilmes finder;
	public void setFinder(ProcuraFilmes finder){
		this.finder = finder;
	}

}
```

Também vamos definir um setter para o nome do arquivo:

```
class ArquivoConsultorDeFilmes {

	public void setNomeArquivo(String nomeArquivo){
		this.nomeArquivo = nomeArquivo;
	}

}
```

O último passo é fazer a configuração, o Spring suporta configurações por arquivos XML e no código também, mas por XML é o mais apropriado.

```
<beans>
    <bean id="ConsultorDeFilmes" class="spring.ConsultorDeFilmes">
        <property name="finder">
            <ref local="ProcuraFilmes"/>
        </property>
    </bean>
    <bean id="ProcuraFilmes" class="spring.ArquivoConsultorDeFilmes">
        <property name="nomeArquivo">
            <value>filmes1.txt</value>
        </property>
    </bean>
</beans>
```

Um exemplo de como testar:

```
public void testComSpring() throws Exception {
    ApplicationContext ctx = new
 
FileSystemXmlApplicationContext("spring.xml");
    ConsultorDeFilmes consultor = (ConsultorDeFilmes)

ctx.getBean("ConsultorDeFilmes");
    Filmes[] filmes = consultor.filmesDirigidosBy("James Cameron");

    assertEquals("Titanic", movies[0].getTitle());
}
```

Finalizando...

Primeiramente gostaria de agradecer a sua curiosidade e paciência em ler até aqui, boa parte do texto foi tradução minha dos textos do [Martin Fowler](https://martinfowler.com/), tentei ao máximo ser fiel às explicações dele, alguns pontos que achei muito complexo, tentei simplificar a explicação para que ficasse mais claro para mim e para ti 😉 e também acrescentei um pouco do meu ponto de vista.

Quero ressaltar aqui também, caso seja user do Spring e suas famosas annotations: as annotations nada mais são do que interfaces, logo, elas seguem o mesmo princípio da inversão de controle mencionado ao longo do texto. Lembre-se que criamos uma bean e no Spring temos o @Bean, aqui a conclusão fica fácil, certo?

Fontes: https://martinfowler.com/articles/injection.html

https://martinfowler.com/bliki/InversionOfControl.html