Qual a diferença entre a inversão e a injeção de dependências?
Tags: java, solid, spring
Published: 2022-06-03 00:37:45

É comum a confusão entre esses dois conceitos. E veremos neste artigo que há alguns bons motivos para isso :D

## O Princípio da Inversão de Dependências

Considere o diagrama abaixo. Ele representa parte de um sistema de negativação de títulos. Quando um devedor deixa de pagar um título, a empresa pode enviar o mesmo para o SERASA.

![Diagrama de parte de nossa solução de negativação de títulos](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fa8lfqi62y7st6w5gj72.jpg)

Note que a classe `Departamento` possui 2 dependências com classes de camadas mais baixas, e que lida com questões tecnológicas. O `Departamento` então busca os títulos a serem negativados em `TitulosDAO` (que acessa o banco de dados) e, de posse da lista de títulos, os envia à `SERASA` (que pode lidar com uma interface REST com o SERASA, por exemplo).

Para representarmos a dependência de uma classe por outra, criamos uma seta que aponte para a direção da dependência. Assim, existem 2 setas, saindo de `Departamento` e apontando para `TitulosDAO` e `SERASA` e, consequentemente, o módulo de uma camada superior está dependendo de módulos de uma camada inferior. Guarde essa informação!

Se pensarmos em classes Java, teremos algo parecido com os códigos abaixo:

```java
package br.com.rodnet.negativador.dao;

import br.com.rodnet.negativador.dominio.Titulo;

import java.util.List;

public class TitulosDAO {

    public List<Titulo> emAtraso() {
        //implementacao
    }
}
```

```java
package br.com.rodnet.negativador.serasa;

import br.com.rodnet.negativador.dominio.Titulo;

import java.util.List;

public class Serasa {
    public void negativar(List<Titulo> titulos) {
    }
}
```

```java
package br.com.rodnet.negativador.dominio;

import br.com.rodnet.negativador.dao.TitulosDAO;
import br.com.rodnet.negativador.serasa.Serasa;

import java.util.List;

class Departamento {

    private TitulosDAO titulosDAO;
    private Serasa serasa;

    Departamento(TitulosDAO titulosDAO, Serasa serasa) {
        this.titulosDAO = titulosDAO;
        this.serasa = serasa;
    }

    void negativar(){
        List<Titulo> titulos = titulosDAO.emAtraso();
        serasa.negativar(titulos);
    }
}
```

Embora essa estrutura seja simples, ela apresenta alguns problemas. A camada superior do sistema depende de abstrações da camada inferior. Consequentemente, se houver alguma alteração em classes como `TituloDAO`, é possível que a classe `Departamento` sofra alterações.

Esse problema parece pequeno e talvez a princípio não mereça tanta atenção, mas é algo bem antigo na nossa profissão. O tão falado livro (não sem razão) do [Uncle Bob](https://www.amazon.com.br/Arquitetura-Limpa-Artesão-Estrutura-Software/dp/8550804606/) cita exemplos de uns 50 anos atrás, onde precisávamos escrever software que precisava, entre outras coisas, escrever nos discos que existiam na época (enormes, barulhentos e com pouca capacidade). E quando eles precisaram trocar os discos para elementos menores (com mais capacidade e, provavelmente, menos barulhentos), os programas precisaram ser reescritos, não somente no código que cuidava da leitura e escrita nos discos, mas em todos os demais códigos que dependiam deles. As vezes, as correções subiam até para camadas mais acima do sistema. 

De lá pra cá, aprendemos (aprendemos?) que é importante que o nosso software utilize as tecnologias adequadas, e que esteja preparado para uma possível substituição delas. 

_"Mas não podemos dizer isso!"_ diria o narrador.

Então podemos dizer que:

> _Módulos de alto nível não devem incorporar (ou incluir) nada dos módulos de baixo nível. Ambos devem trabalhar apenas com abstrações (interfaces ou classes abstratas).
Abstrações não devem depender de detalhes. Detalhes é que devem depender de abstrações._

Assim, se revisitarmos as classes do nosso sistema, podemos concluir que as classes de alto nível dependem de classes de baixo nível (`Departamento` depende de `TitulosDAO` e de `SERASA`). Portanto, mudanças devem ser feitas.

Segundo a citação acima, o `Departamento` deveria depender de abstrações. No caso, abstrações das classes que ele depende hoje.

Para que isso seja possível, não temos muitas alternativas a não ser criarmos interfaces que sejam implementadas pelas dependências atuais de `Departamento`. Então, mãos à obra:

Podemos começar pelo `TitulosDAO`, [extraindo uma interface](https://refactoring.guru/pt-br/extract-interface) do mesmo:

```java
package br.com.rodnet.negativador.dominio;

import java.util.List;

public interface RepositorioTitulos {
    List<Titulo> emAtraso();
}
```
```java
package br.com.rodnet.negativador.dao;

import br.com.rodnet.negativador.dominio.RepositorioTitulos;
import br.com.rodnet.negativador.dominio.Titulo;

import java.util.List;

public class TitulosDAO implements RepositorioTitulos {

    @Override
    public List<Titulo> emAtraso() {
        //implementacao
        return null;
    }
}
```

e fazer o mesmo com `SERASA`:

```java
package br.com.rodnet.negativador.dominio;

import java.util.List;

public interface ServicoDeProtecaoAoCredito {
    void negativar(List<Titulo> titulos);
}
```
```java
package br.com.rodnet.negativador.serasa;

import br.com.rodnet.negativador.dominio.ServicoDeProtecaoAoCredito;
import br.com.rodnet.negativador.dominio.Titulo;

import java.util.List;

public class Serasa implements ServicoDeProtecaoAoCredito {
    @Override
    public void negativar(List<Titulo> titulos) {
    }
}
```

Agora que temos abstrações das classes de camadas inferiores, podemos agora substituir as dependências de `Departamento` por elas:

```java
package br.com.rodnet.negativador.dominio;

import java.util.List;

class Departamento {

    private RepositorioTitulos repositorioTitulos;
    private ServicoDeProtecaoAoCredito servicoDeProtecaoAoCredito;

    Departamento(RepositorioTitulos repositorioTitulos, ServicoDeProtecaoAoCredito servicoDeProtecaoAoCredito) {
        this.repositorioTitulos = repositorioTitulos;
        this.servicoDeProtecaoAoCredito = servicoDeProtecaoAoCredito;
    }

    void negativar(){
        List<Titulo> titulos = repositorioTitulos.emAtraso();
        servicoDeProtecaoAoCredito.negativar(titulos);
    }
}
```

**OBS:** troquei também os nomes dos atributos para que ficassem mais condizentes com as novas abstrações.

Percebam que agora as classes `TitulosDAO` e `SERASA` podem ser alteradas à vontade, pois isso não acarretará em alterações em `Departamento`. Elas só precisam implementar as interfaces de domínio, da maneira que for melhor para elas. 

Podemos inclusive pensar mais longe:

- trocamos de empresa de negativação? Sem problemas. Basta criar uma nova classe que implemente `ServicoDeProtecaoAoCredito` 
- não estamos mais usando um banco de dados, mas um serviço externo de busca de títulos? Tudo bem! Basta trocarmos a classe que implementa `RepositorioTitulos`. O `Departamento` não deverá sofrer nenhuma alteração sequer!

Agora, observem como nosso diagrama ficou após todas essas mudanças:

![Modelagem da solução após as refatorações realizadas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7r4zcd0xj27z5snwan7b.jpg)

Naturalmente que existem mais elementos no nosso diagrama. Afinal, precisamos representar nossas novas interfaces. Mas reparem para as setas. Mais precisamente as setas que indicam herança (ou implementação, no nosso caso).

Lembra que pedi para você guardar a informação sobre o sentido das setas? Pois bem: antes, as setas apontavam para baixo. E agora, elas apontam para cima. Ou seja, houve uma inversão de quem depende de quem. Ou, se preferir, **houve uma inversão das dependências**. Agora são os módulos das camadas inferiores é que dependem dos módulos superiores.

Basicamente esse é o [Princípio de Inversão de Dependências](https://deviq.com/principles/dependency-inversion-principle). E a sua definição mais básica é aquela citação feita lá em cima:

> Módulos de alto nível não devem incorporar (ou incluir) nada dos módulos de baixo nível. Ambos devem trabalhar apenas com abstrações (interfaces ou classes abstratas).
Abstrações não devem depender de detalhes. Detalhes é que devem depender de abstrações.

## E a injeção de dependências?

Bem, a injeção de dependências tem mais a ver com a maneira com que criamos nossos objetos. 

Vamos ver novamente a classe `Departamento`:

```java
package br.com.rodnet.negativador.dominio;

import java.util.List;

class Departamento {

    private RepositorioTitulos repositorioTitulos;
    private ServicoDeProtecaoAoCredito servicoDeProtecaoAoCredito;

    Departamento(RepositorioTitulos repositorioTitulos, ServicoDeProtecaoAoCredito servicoDeProtecaoAoCredito) {
        this.repositorioTitulos = repositorioTitulos;
        this.servicoDeProtecaoAoCredito = servicoDeProtecaoAoCredito;
    }

    void negativar(){
        List<Titulo> titulos = repositorioTitulos.emAtraso();
        servicoDeProtecaoAoCredito.negativar(titulos);
    }
}
```

Em Java, independente do fato de usarmos ou não um _framework_, os objetos devem ser instanciados de alguma forma. E as formas mais conhecidas são:

- instanciação simples: via o famoso operador `new`
- [instanciação via API de reflection
](http://www.java2s.com/ref/java/java-reflection-constructor-instantiate-objects.html)
- instanciação via framework de injeção de dependências

Agora, pense no caso da classe `Departamento`. Se usarmos o operador new para instanciá-la, podemos fazer algo como:

```java
var repositorioTitulos = new TitulosDAO();
var servicoDeProtecaoAoCredito = new Serasa();
var departamento = new Departamento(repositorioTitulos, servicoDeProtecaoAoCredito);
```

E quem seria responsável por instanciar todas essas classes?
No passado, usávamos bastante os padrões criacionais do [GoF](https://www.amazon.com.br/Padrões-Projetos-Soluções-Reutilizáveis-Orientados/dp/8573076100) (como [Abstract Factory](https://refactoring.guru/pt-br/design-patterns/abstract-factory) ou [Factory Method](https://refactoring.guru/pt-br/design-patterns/factory-method)). Depois, passamos a trabalhar com o _pattern_ [Registry](https://java-design-patterns.com/patterns/registry/) e até mesmo com [JNDI](https://docs.oracle.com/javase/tutorial/jndi/overview/index.html). E, resumidamente falando, tínhamos mais desvantagens do que vantagens. 

Talvez num cenário com apenas 3 classes, os problemas não sejam tão visíveis, mas pense num sistema maior, com mais classes. Um grande problema era o fato de que essas classes "fábrica" viviam em constante mudança, o que as tornavam bastante instáveis. E devemos evitar mudanças constantes, tanto em classes quanto em módulos inteiros. Para maiores detalhes e informações sobre o problema de mudanças em módulos, vale a pena estudar:

- [o Princípio das Dependências Estáveis](https://devlead.io/DevTips/StableDependenciesPrinciple)
- [o Princípio das Abstrações Estáveis](https://devlead.io/DevTips/StableAbstractionsPrinciple) 

No fundo, o SOLID também se preocupa com o excesso de mudanças, mas em classes. E por falar em SOLID, o Uncle Bob também fala sobre os princípios acima [no seu livro](https://www.amazon.com.br/Arquitetura-Limpa-Artesão-Estrutura-Software/dp/8550804606).

Bem, feita a tangente e voltando para o problema principal: se você conhece o Spring, normalmente você resolveria o problema de criação das classes inserindo as famosas anotações `@Autowired` e, no mínimo `@Component` ou alguma outra parecida nas dependências. Algo como:

```java
@Service
class Departamento {

    private RepositorioTitulos repositorioTitulos;
    private ServicoDeProtecaoAoCredito servicoDeProtecaoAoCredito;

    @Autowired
    Departamento(RepositorioTitulos repositorioTitulos, ServicoDeProtecaoAoCredito servicoDeProtecaoAoCredito) {
        this.repositorioTitulos = repositorioTitulos;
        this.servicoDeProtecaoAoCredito = servicoDeProtecaoAoCredito;
    }

    void negativar(){
        List<Titulo> titulos = repositorioTitulos.emAtraso();
        servicoDeProtecaoAoCredito.negativar(titulos);
    }
}
``` 

e nas demais classes:

```java
@Repository
public class TitulosDAO implements RepositorioTitulos {

    @Override
    public List<Titulo> emAtraso() {
        //implementacao
        return null;
    }
}
```

```java
@Component
public class Serasa implements ServicoDeProtecaoAoCredito {
    @Override
    public void negativar(List<Titulo> titulos) {
    }
}
```

Repare que, quando você utiliza um _framework_ como o spring, você não precisa instanciar essas classes, muito menos passá-las como parâmetro para `Departamento`. O spring as instancia e as "injeta" em `Departamento`, que também pode ser injetada em outra classe. Assim, ele consegue montar boa parte da árvore de objetos necessária para o funcionamento do sistema. 

Naturalmente que ainda temos que instanciar manualmente algumas classes. Mas não podemos negar que a injeção de dependências é uma mão na roda quando temos que construir objetos com uma árvore de dependências complexa (dependência que depende de dependência, que depende de dependência...).

E aí? Ficou mais clara a diferença entre esses dois conceitos? Normalmente eles são usados juntos, e talvez por isso crie-se tanta confusão entre eles.

Até a próxima!