Ao longo dos anos, na busca por aperfeiçoar e compartilhar conhecimento, conversamos com arquitetos e desenvolvedores em conferências e fóruns sobre como esses resolvem alguns problemas comuns durante o desenvolvimento de software, principalmente aqueles relacionados à arquitetura das aplicações. O que constatamos é que a maioria faz uso de padrões já conhecidos, em geral adaptando-os às suas necessidades. Em sistemas de grande porte, onde há várias camadas e componentes compondo uma arquitetura complexa, essas soluções visam principalmente auxiliar no controle das dependências no projeto, ou seja, dependências entre objetos, entre comandos e ações, entre eventos e entre artefatos de todos os tipos.
Para a maior parte dos problemas arquiteturais que conhecemos, existe um conjunto de padrões de desenvolvimento que fornecem orientações úteis e que ajudam os desenvolvedores a construírem, principalmente, sistemas fracamente acoplados com um mínimo de dependência entre os componentes. Esses padrões auxiliam, entre outros fatores, na criação e uso de objetos que implementam certas responsabilidades, como os padrões Factory, Builder, Lifetime, Inversão de Controle (IoC, do inglês Inversion of Control) e Injeção de Dependências (ou DI - Dependency Injection).
O Microsoft Unity
O Unity é um container para injeção de dependências leve e extensível com suporte a injeções por construtor, propriedade e chamada de métodos. Seu uso facilita a construção de aplicações com fraco acoplamento e garante algumas vantagens ao desenvolvedor, a saber:
· Criação de objetos simplificada, especialmente para estruturas hierárquicas e dependências;
· Abstração de requisitos, que permite aos desenvolvedores informarem as dependências durante a execução ou via configuração, além de gerenciamento simplificado;
· Aumenta a flexibilidade por transferir a configuração dos componentes do ambiente do ciclo de vida para o container;
· Capacidade de localização de serviços, o que permite aos clientes acessarem o container ou seu cache;
Esse container pode ser usado em qualquer aplicação .NET e dispõe de todas as funcionalidades encontradas em mecanismos de injeção de dependência, tais como métodos para registrar e mapear tipos e instâncias, resolver e gerenciar o ciclo de vida de objetos, os injetando via parâmetros em construtores, métodos e valores de propriedades.
No Unity também temos a liberdade para criar extensões que alteram o comportamento do container e adicionam novas funcionalidades. Por exemplo, a funcionalidade de interceptação disponível no Unity, que podemos utilizar para capturar chamadas de objetos e adicionar funcionalidades e políticas para os objetos alvo, é uma implementação de uma extensão do container.
Tipos de Injeção de Dependências utilizados com o Unity
Injeção por construtor
Normalmente, quando instanciamos um objeto, invocamos o construtor da classe com o operador new e passamos qualquer valor que o objeto precisa como parâmetro. Na Listagem 1 a classe ContatoDao espera receber em seu construtor um objeto que implemente a interface ISessionFactory. Esse é um exemplo de injeção via construtor e é o tipo mais utilizado. Observe que a classe não depende de uma implementação concreta para ser instanciada, ela agora necessita apenas que um certo contrato (interface) seja seguido para que o parâmetro seja compatível com o esperado no construtor. Esse será o tipo utilizado neste artigo.
Listagem 1. Exemplo de Injeção via Construtor
01 public class ContatoDao: DaoAbstratoGenerico<Contato>, IContatoDao
02 {
03 public ContatoDao(ISessionFactory sessionFactory)
04 :base(sessionFactory)
05 {
06
07 }
08 }
09 public void InjecaoViaConstrutor()
10 {
11 container.RegisterType<ObjetosDeApoio.IContatoDao, ObjetosDeApoio.ContatoDao>(
12 new InjectionConstructor(HibernateUtil.FabricaDeSessao)
13 );
14
15 ObjetosDeApoio.IContatoDao dao = container.Resolve<ObjetosDeApoio.IContatoDao>();
16
17 Assert.IsTrue(dao.BuscarTodos().Count > 0);
18 }
Nas linhas 11 a 13 dessa listagem estamos utilizando o módulo de injeção via construtor do Unity, informando que os objetos do tipo IContatoDao serão instanciados como ContatoDao (classe concreta que implementa essa interface) e para eles deverá ser passado o objeto HibernateUtil.FabricaDeSessao, que implementa a interface ISessionFactory esperada como parâmetro na linha 3. Em seguida, na linha 15 testamos o mecanismo que configuramos, criando um objeto do tipo IContatoDao a partir do método Resolve do container, que irá criar retornar um objeto do tipo ContatoDao com a fábrica de sessão já instanciada. Na linha 17 apenas verificamos se o objeto foi instanciado corretamente, avaliando se ele realmente consegue acessar a base de dados e retornar os registros esperados.
Injeção via propriedade
O Unity, como outros containers robustos do mercado, também trabalha com a possibilidade de injeção via propriedade. Mesmo que o recomendado seja trabalhar com a injeção via construtor, podem ocorrer situações onde só há a possibilidade de utilizar este segundo tipo, que em outros containers também é chamado de injeção via setter.
Existem casos em que a injeção via propriedade é recomendada:
· Quando precisamos instanciar objetos dependentes automaticamente quando instanciamos o objeto pai;
· Quando queremos uma forma simples, que torna fácil a leitura do código, para encontrar as dependências de cada classe envolvida;
· Quando o objeto pai precisa de um grande número de construtores que acessam vários objetos e tornam a manutenção e a depuração mais difíceis;
· Quando o construtor da classe base necessita de um grande número de parâmetros, especialmente se eles são de tipos parecidos e existe apenas um modo de identificá-los por sua posição;
Na Listagem 2 temos um exemplo de implementação desse tipo de injeção, mostrando primeiro a classe com sua propriedade do tipo ISessionFactory (linhas 1 a 12) e depois um teste unitário com a implementação.
Listagem 2. Injeção por propriedade
01 public class ContatoDaoProp ...