Uma das maiores dúvidas do pessoal que está iniciando no entendimento do Java EE, é entender os conceitos e padrões definidos pela comunidade. Neste “post” tentarei explicar o que é e, para que serve a Inversão de controle com injeção de dependências em arquitetura e desenho de aplicações. Por uma questão exemplar, os códigos aqui contidos não estarão no formato “copy-paste-and-compile-success”, mas irão lhe dar um ótimo empurrão para sair caçando materiais Internet à fora, etc. Os exemplos serão detalhados na linguagem de programação Java.
Martin Fowler, escreveu um bom artigo em seu site sobre estes dois padrões, recomendo como uma leitura introdutória a esta (ou vice-versa), não se preocupe que lá tem um link para uma versão em português. Como para mim, não será suficiente para você (caso ainda não entenda bulufa de nada disso), então recomendo a não parar por aí e realmente usar na prática os padrões. O link pode ser conferido aqui: http://martinfowler.com/articles/injection.html
Muitas pessoas costumam confundir inversão de controle com injeção de dependências, até mesmo tem quem o diga que se trata da mesma coisa. Mas não é bem assim (você verá o porquê mais adiante, pois não foi à toa que prego: Inversão de controle COM injeção de dependências).
O pior defeito de quem inicia neste ramo é sempre procurar entender um conceito a fundo sem ao menos parar para pensar no próprio nome do conceito, este por sinal já pode ser tudo que lhe seja necessário para entender o tal. É assim com Inversion of control and Dependency Injection.
- Inversão de controle: O que viria ser inverter o controle? Vamos pensar o seguinte: Há um cargo em sua empresa de gestor de TI, este cargo exige que a pessoa saiba controlarPessoas(), administrarRecursos() e gerenciarOSetor(). Você contrata um bom profissional para executá-lo (José) conforme o perfil acima e o coloca no CONTROLE. Passa dois anos e aquela pessoa começa a demonstrar deficiências em determinado aspecto, ai então você contrata outra pessoa (João) com as qualificações necessárias e.. e.. INVERTE O CONTROLE da gestão de TI da empresa! (Pobre José!) Pode parecer um tanto exótico este exemplo, mas você já conseguiu notar que as minhas simples colocações em forma de métodos nas habilidades do cargo já começam a esclarecer a ligação com a forma prática (ou seria programática) de se fazer.
- Injeção de dependências: Seguindo nosso mesmo estranho exemplo, vamos supor que você não quer saber de cuidar do RH da sua empresa, e coloca um profissional para tal, então a partir daí quem faz o papel de contratar, demitir e é ele. Logo, quando um executor de determinado cargo precisa ser invertido, este profissional escolhe outro e .. e.. INJETA A DEPENDÊNCIA no cargo, conseqüentemente invertendo o controle.
(Neste momento estou pensando se eu confundi mais do que ajudei… )
Vamos fazer o seguinte, .. vou começar a falar mais tecnicamente e cada vez que você não entender nada, sobe até os últimos dois tópicos e leia os conceitos que até aqui parecem estranhos.
Desenho orientado a interfaces
Sabemos que uma das boas práticas (blueprints) no desenvolvimento de aplicações, é esquecer da implementação no momento que estiver especificando. Isso torna seu projeto INDEPENDENTE de implementação, logo você está promovendo o baixo acoplamento. Ta, ta, parei.
Vamos lá, com base no nosso exemplo em português (ou quase) estruturado acima, temos:
interface Gestor {
void controlarPessoas();
void administrarRecursos();
void gerenciarOSetor();
}
//este setor é responsável por inverter controles
class RH {
void escolherGestor(Gestor gestor) {
minhaEmpresa.setGestor(gestor);
}
}
class João extends Pessoa implements Gestor {
//considere os metodos implementados aqui..
}
class Jose extends Pessoa implements Gestor {
//e aqui
}
class Aplicação {
public static void main(String [] argumentos) {
RH rh = new RH();
//antes era o José, mas hoje é o João
//Apenas invertemos o controle, mas não injetamos a dependência
Gestor gestor = new João(); // new Jose();
Rh.escolherGestor(gestor);
}
}
(Aqui você deve estar procurando entender a utilidade disso)..
Veja que, quando nós alteramos uma implementação, o que é alterado é apenas o que vem depois do “=”, polimorficamente falando, pois tanto como João quanto Jose implementam Gestor e podem estar ali. Mas o foco do padrão de inverter o controle de determinada missão não é isso. O foco é além de ter o baixo acoplamento, é centralizar esta configuração em um único lugar. É aí que entra os FRAMEWORKS DE INJEÇÃO DE DEPENDÊNCIAS. Entre eles, posso ressaltar o Spring, PicoContainer, Guice, entre outros muito utilizados pela comunidade. O que eles fazem é o papel do RH do nosso exemplo, se tornando nossos empregados para cuidar de injetar as dependências das nossas classes e/ou inverter o controle de outras, quando necessário.
De uma forma exemplar, vamos considerar o seguinte: Dentro da nossa aplicação existe 400 lugares onde o Gestor é instanciado usando a implementação do José (Gestor gestor = new Jose()), quando resolvermos inverter o controle, teríamos um duro trabalho de refatorar ponto-a-ponto e substituir new Jose() por new João(). Isso seria extremamente chato uma vez que você possa pensar na possibilidade do João um dia não atender o requerido e ter que ser substituído novamente, em outras palavras: re-trabalho. Mas pense, não seria fantástico se pudéssemos centralizar a escolha de quem vai implementar Gestor em um arquivo só, e alguém mais responsável que nós, fizesse esse trabalho árduo de injetar essa implementação ? É exatamente desta forma que os frameworks trabalham.
Nosso arquivo de configuração aqui nomeado: framework-config.xml,
Dentro dele a seguinte estrutura:
<IoC classe=br.com.Gestor>
<DI injete=br.com.Jose/>
</IoC>
Agora nos 400 lugares da nossa aplicação onde é necessário uma injeção de dependências explicitas, não iremos mais nos preocupar com a instanciação da implementação, deixaremos isso com o framework.
Considerando a nossa classe que possui membros que precisam receber as injeções:
class RH {
Gestor gestor; //note que aqui há uma DEPENDÊNCIA de uma implementação
}
Bom, como pode ter sido analisado no xml de configuração do nosso pseudo-framework cada vez que você criar a classe RH o container do seu framework vai percorrer de alguma forma o xml de configuração e verificar onde injetar e o que injetar. Ex:
RH rh = meuFrameworkDeDIPreferido.instancieEInjecteAsDependencias(RH.class)
(Nota: Ficaria melhor criar uma Factory para isso, assim você também ficaria independente de framework de DI, algo como: RH rh = Factory.getInstance(“rh”); e o getInstance cuidaria de quem iria construir a sua classe.)
Feito isso, podemos ficar tranqüilos que o nosso framework a partir daí, irá se encarregar de fazer o restante do trabalho (não fique com medo que você não receberá nenhum NullPointerException ao tentar utilizar gestor).
Desta forma, o dia que precisarmos substituir Jose por João, apenas iríamos alterar nosso arquivo xml, e substituir a linha “<DI injete=br.com.Jose/>” por “<DI injete=br.com.João/>” e puff, as dependências continuariam sendo injetadas e o controle daquela interface seria invertido sem alterar absolutamente nada no código.
Espero que tenham entendido este conceito tão importante nos dias de hoje e não deixem de deixar um feedback caso continuarem com dúvidas. Valeu, até a próxima!
Robson
Referências:
http://martinfowler.com/articles/injection.html
http://www.picocontainer.org/injection.html
http://static.springframework.org/spring/docs/2.0.x/reference/beans.html
Olá,
Parabéns pelo artigo, simplesmente fantástico para entender. Só fiquei com uma dúvida:
Você diz:
”
//antes era o José, mas hoje é o João
//Apenas invertemos o controle, mas não injetamos a dependência
Gestor gestor = new João(); // new Jose();
”
Em que ponto é feita de fato a injeção de dependencia?
Obrigado e mais uma vez parabéns pelo artigo.
Olá Marcelo, Obrigado.
Veja que quando citei “Apenas invertemos o controle” significa que, de fato, não foi feita uma injeção de dependências, pois está explicito no código o “new João()..”, se fosse, neste caso, via ID, teríamos algo como:
private Gestor gestao;
public void setGestor(Gestor gestor) {
this.gestor = gestor;
}
E no nosso container de DI, uma configuração como:
E, quando chamassemos nosso assembler de construção de classes do container (algo como: Rh rh = SpringUtil.getContext().getBean(“Gestor”)), o container saberia aplicar ao determinado campo, sua implementação configurada, possibilitando a inversão mais simples de controle, uma vez que bastasse você alterar o XML ao invés de abrir o código, mudar e ter que recompilar tudo novamente.
[...] Note que foi usado uma inversão de controle no constructor do Celular, mas também pode ser criado métodos de acesso a variavel de instância para que possa ser encapsulado isso. O ideal mesmo, é que se utilize um container de Injeção de dependências, como o Spring. Explicado em outro artigo. [...]
Fantástico…estou a 4 dias tentando entender esses conceitos e só consegui entender com a sua explicação…parabéns.
Obrigado.
Gostei da apresentação dos conceitos.
Parabéns pelo blog!
=)
Detonou !
E não é que entendi ?!
Gostei da sua forma de explicar..
Parece q estávamos conversando….
Muito bom !
obrigada
Excelente texto!! com exemplos faceis de serem entendidos!!
Continue assim, pois, é bem mais facil entender um conceito e desenvolver seus codigos, a copiar os códigos para entender um conceito!