Inversão de controle x injeção de dependência x inversão de dependência
Entenda a diferença entre inversão de controle, injeção de dependência e inversão de dependência.
Nesse artigo vou explicar a diferença entre três conceitos muito usados
no mundo da programação: a inversão de controle, a injeção de dependência e a
inversão de dependência. Notou como esses três nomes são parecidos? Pois bem,
apesar de parecidos esses nomes significam elementos distintos no mundo do
desenvolvimento de sistemas, muitas
pessoas confundem esses conceitos, outras sequer sabem da existência de todos
os três e existem aquelas ainda que pensam que esses conceitos são sinônimos. O
objetivo desse artigo e explicar de forma clara o que são cada um desses
conceitos e como eles se relacionam entre si.
Primeiramente entenda:
Inversão de controle é um princípio de design.
Injeção de dependência é um padrão de projetos.
Inversão de dependência é um dos cinco princípios da programação
orientada a objetos (SOLID).
Vamos nos aprofundar em cada conceito:
1. Inversão de controle
A inversão de controle é um padrão de projeto que prega remover de uma classe a responsabilidade de instanciar outras classes e passar essa responsabilidade para um componente externo do sistema, sendo ele um framework, um container, uma classe, um serviço e etc.
Exemplo:
public class VendaDeProduto {
public void vendeProduto(Produto produto) {
//Todo o código para a venda do produto...
Log log = new Log("Arquivo.txt");
log.grava(produto);
}
}
Imaginamos que tenhamos que mudar o construtor da classe Log para além
de receber o nome do arquivo também tenha que receber o endereço do diretório
que o arquivo se encontra, nesse caso, basta fazer a alteração na classe
VendaDeProduto, mas e se tivermos umas
trinta classes utilizando a classe Log? Teríamos que alterar todas as trinta, o que seria um
esforço gigantesco.
Para resolver esse problema é que utilizamos a inversão de controle,
onde a classe VendaDeProduto não vai mais instanciar a classe Log e sim outro
componente do sistema, no caso em questão essa responsabilidade agora é da
classe Ecommerce.
public class Ecommerce {
public static void main(String[] args) {
Produto produto = new Produto();
Log log = new Log("Arquivo.txt", "C:\\logs");
VendaDeProduto novavenda = new VendaDeProduto(produto, log)
}
}
Veja que para a implementação acontecer precisamos adicionar um objeto
Log no construtor da classe
VendaDeProduto, ao utilizar essa técnica nós entramos no conceito de
injeção de dependência que é uma das formas de se fazer a inversão de controle.
2. injeção de dependência
A injeção de dependência é um padrão de projetos que visa utilizar uma
estrutura de software (container) para "injetar" as dependências
usadas por uma classe em vez de deixar com essa classe a responsabilidade de
instanciá-las (entenda por dependência
como os atributos e métodos de outras classes ), nessa definição
você notou a correlação com a inversão
de controle? Como dito antes a injeção de dependência é uma forma de se
realizar a inversão de controle.
Além da forma citada acima existem outras duas formas de se realizar a
injeção de dependência, vou descrever cada uma das três:
INJEÇÂO POR CONSTRUTOR
Acontece quando a classe (módulo) a ser utilizada recebe por construtor
uma instância de classe que ela mesma irá utilizar (utilização = dependência).
No nosso caso, precisamos utilizar o
método construtor de
VendaDeProduto, para isso, a instância de Produto é enviada através do
construtor de VendaDeProduto.
O nosso exemplo utilizado para demostrar a inversão de controle é uma
injeção de dependência por construtor.
public class VendaDeProduto {
private Produto produto;
private Log log;
public VendaDeProduto(Produto produto, Log log) {
this.produto = produto;
this.log = log;
}
public void vendeProduto() {
//Todo o código para a venda do produto...
log.grava(produto);
}
}
INJEÇÃO POR PROPRIEDADE
Esse tipo de injeção ocorre quando se tem o objeto a ser injetada
exposta como um atributo da classe utilizadora.
Classe VendaDeProduto:
Classe VendaDeProduto:
public class VendaDeProduto {
private Produto produto;
private Log log;
public VendaDeProduto() {
}
public Produto getProduto() {
return produto;
}
public void setProduto(Produto produto) {
this.produto = produto;
}
public Log getLog() {
return log;
}
public void setLog(Log log) {
this.log = log;
}
public void vendeProduto() {
//Todo o código para a venda do produto...
log.grava(produto);
}
}
Classe Ecommerce:
public class Ecommerce {
public static void main(String[] args) {
Produto produto = new Produto();
Log log = new Log("Arquivo.txt", "C:\\logs");
VendaDeProduto novavenda = new VendaDeProduto();
novavenda.setLog(log);
novavenda.setProduto(produto);
}
}
INJEÇÂO POR INTERFACE
Ao invés de receber o objeto por parâmetro, agora a classe deve receber
uma abstração da implementação que ela irá utilizar, através de uma interface.
O grande benefício é que quem define qual implementação da abstração será
utilizada é a classe que está chamando a
classe dependente.
Classe VendaDeProduto:
public class VendaDeProduto {
private IProduto iproduto;
private ILog ilog;
public VendaDeProduto(IProduto iproduto, ILog ilog) {
this.iproduto = iproduto;
this.ilog = ilog;
}
public void vendeProduto() {
//Todo o código para a venda do produto...
ilog.grava(iproduto);
}
}
Classe Log:
public class Log implements ILog{
public Log() {
// TODO Auto-generated constructor stub
}
public Log(String string, String string2) {
// TODO Auto-generated constructor stub
}
public void grava(IProduto produto) {
// TODO Auto-generated method stub
}
}
Classe Produto:
public class Produto implements IProduto {
public Produto() {
// TODO Auto-generated constructor stub
}
}
Exemplo:
Em nosso exemplo, se quisermos deixar de utilizar a classe Log para
utilizar uma classe que registre as informações de usuário dentro de uma base
de dados basta substituirmos a classe Log, enviada como parâmetro para o construtor
de VendaDeProduto, por uma classe que faça a persistência no banco de dados e
que também implemente a interface ILog.
Esse exemplo de injeção de dependências por meio de interfaces é o que
chamamos de inversão de dependência.
3. INVERSÃO DE DEPENDÊNCIA
Como descrito no início desse artigo a inversão de dependência é um dos
cinco princípios da programação orientada a objetos - SOLID, representada pela letra
"D" de Dependency Inversion
Principle (DIP).
A principal definição do princípio da inversão de dependência diz:
"Módulos de alto nível não devem depender de módulos de baixo nível e
ambos devem depender de abstrações; e abstrações não devem depender de
detalhes, mas detalhes devem depender de abstrações.".
Para uma definição mais clara podemos dizer: As classes mais externas de
um sistema não devem depender das classes mais fundamentais do sistema e ambas
devem depender de interfaces e interfaces não devem depender de detalhes, mas
detalhes devem depender de interfaces.
Em nosso exemplo podemos notar que antes de usarmos a inversão de
dependência a classe venda de produto dependia diretamente da classe Log para
funcionar corretamente como mostra a figura abaixo.
Após a inversão de dependência, a classe VendaDeProduto não depende mais
de Log, mas sim de uma interface (abstração) chamada de ILog, Como Log é uma implementação de ILog,
podemos enviá-la sem problemas ao construtor de VendaDeProdutos e se quisermos
substituir a classe Log por outra classe basta fazer essa nova classe estender
ILog para podermos enviá-la via construtor
para VendaDeProdutos sem problemas.
Conclusão
Podemos concluir que a inversão de dependência é uma forma de injeção de
dependência e que a injeção de dependência por sua vez é uma forma de inversão
de controle.
Espero que esse artigo tenha sido esclarecedor para muitas pessoas em
relação ao tema, qualquer dúvida deixe nos comentários ou entre em contato.
Comentários
Postar um comentário