Atenção: esse artigo tem um vídeo complementar. Clique e assista!

De que se trata o artigo

Este artigo mostra o que é e como usar inversão de controle e a injeção de dependência. Você aprenderá na prática, através de um exemplo, como estes princípios de programação orientada a objetos podem ser usados e qual as vantagens que eles trazem.


Em que situação o tema é útil

A inversão de controle torna um projeto mais escalável e simples de entender. Este é um conhecimento obrigatório para qualquer programador que se preze. Este artigo ajudará você a entender porque a herança deve ser evitada explicando onde a inversão de controle pode ser usada para substituí-la.

Inversão de Controle e Injeção de Dependência

Como desenvolvedores de software, estamos sempre preocupados em criar um projeto escalável, e nos esforçamos para isto. Para atingir este objetivo é possível usar padrões, técnicas e até filosofias de desenvolvimento diferentes. Indiferente de como sua equipe escolheu trabalhar, todo projeto flexível tende para o mesmo lado: diminuir o acoplamento entre as classes. Este artigo explica como usar a inversão de controle para diminuir o acoplamento e apresenta algumas opções para injetar automaticamente as dependências de uma classe a fim de conseguir um projeto mais flexível.

Diz-se que um projeto está altamente acoplado quando suas classes e módulos conhecem muito um do outro, criando uma grande dependência entre eles. Isto acontece, na maioria das vezes, devido a uma decisão errada, tomada pelo projetista, que aos poucos encaminha o projeto para um design caótico onde uma simples manutenção de rotina pode desencadear uma série de erros em outro ponto do projeto.

O grande vilão, que pode tornar uma alteração pequena em um verdadeiro desastre é o relacionamento entre as classes, por isso é vantajoso investir o tempo necessário (ou possível), para criar designs onde as classes sejam capazes de se relacionar conhecendo o mínimo uma da outra. Criar classes coerentes que dependem apenas de interfaces é uma das formas de garantir o baixo acoplamento, porém o princípio “programe para interface”, sozinho não ajudará muito.

O problema do alto acoplamento

Podemos medir o quão fortemente as classes de uma aplicação estão conectadas, possuem conhecimento ou dependem de outra classe observando o acoplamento existente em cada caso. Quanto mais acoplada uma classe estiver de outra maior o risco de termos uma combinação dos seguintes problemas:

· Mudanças numa classe relacionada forçam alterações locais à classe para que o funcionamento seja mantido;

· O reuso da classe torna-se mais difícil uma vez que sua implementação depende de outras classes;

· A coesão da classe diminui na medida em que aumenta o acoplamento caso ela assuma parte das responsabilidades de outra classe;

· A classe não pode ser testada isoladamente já que depende de testes em conjunto com outras classes, aumentando inclusive o esforço de entendimento.

Percebemos rapidamente que diminuindo ao máximo o acoplamento além de evitarmos os problemas mencionados anteriormente tornaremos nossa aplicação mais flexível e fácil de ser mantida, especialmente em equipes com paralelismo de desenvolvimento de atividades correlatas.

Utilizando as corretas técnicas e ferramentas para injeção de dependência, diversos componentes podem ser desenvolvidos paralelamente (aproveitando de recursos como Mock Objects para auxiliar nos testes unitários) e sua aplicação se tornará mais plugável e flexível, exigindo um esforço menor para modificações.

Técnicas para resolver o problema de alto acoplamento

Tomemos como exemplo uma classe chamada ClasseA que depende dos serviços Servico1 e Servico2, conforme a Figura 1.

Figura 1. Dependência entre classes

Se em algum momento necessitarmos substituir ou atualizar alguma das suas dependências, a classe ClasseA sofrerá alterações em seu código, uma vez que está relacionada diretamente com as classes de serviço. Outro ponto é que as implementações concretas das dependências precisam estar disponíveis no momento da compilação da solução. Uma das soluções seria delegar a função de selecionar a implementação da classe concreta para um componente (ou um container) externo.

Em diversas literaturas podemos encontrar esta solução sendo descrita como Padrão de Inversão de Controle (Inversion of Control Pattern, ou simplesmente IoC), onde a Injeção de Dependência é uma das maneiras de a implementarmos (assim como o Service Locator Pattern), conforme observamos na Figura 2. Sendo assim, é correto afirmarmos que a DI é um caso particular de IoC.

A classe ClasseA ao invés de fazer referência à classe concreta passa a referenciar uma interface IServicoA que por sua vez é implementada pelo serviço Servico1, o objeto identificado como Builder fica encarregado de resolver a classe concreta e injetar a dependência na classe ClasseA.

...

Quer ler esse conteúdo completo? Tenha acesso completo