{tecnologia, conceitos, negócios, idéias, práticas, .NET, ruby, osx, ios e algo mais}
18/01/2010
Bom pessoal, pudemos ver os benefícios e alguns usos de Inversão de Controle e Injeção de Dependências aqui e aqui.Uma das formas de obter excelentes ganhos com a inversão de controle é através da utilização de um contêiner de Injeção de Dependências.Um contêiner de injeção de dependências é capaz de criar objetos com todas suas dependências injetadas e totalmente pronto para uso. Em geral estes conteiners podem ser configurados manualmente(programaticamente) ou dinamicamente(através de arquivos de configuração por exemplo).Falaremos um pouco do Unity que é um contêiner de Injeção de Dependência que faz parte dos Application Blocks da Microsoft.Para que vejamos como o Unity funciona faça o download do mesmo aqui e execute o setup, que irá apenas criar uma pasta com as DLLs do Unity.O Unity, como veremos nos exemplos, suporta 3 tipos de injeção de dependência:- Constructor Injection (injeção por construtor) - Property Injection (injeção de propriedade) - Method Call Injection (injeção de chamada de métodos) Vamos usar como exemplo estas classes e interfaces:
public interface ILogger{
void RegistrarMensagem(string mensagem);
}
public class SqlLogger : ILogger{
public void RegistrarMensagem(string mensagem) { //abre conexão SQL //Executa insert da mensagem }
}
public class EnviadorDeEmails{
public ILogger Logger { get;
set;
}
public EnviadorDeEmails(ILogger logger) { this.Logger = logger;
}
public void EnviarEmail(string email, string mensagem) { //Envia email //registra envio this.Logger.RegistrarMensagem(string.Format("Email enviado para {
}
", email));
}
}
Adicione as seguintes referências ao seu projeto: Microsoft.Practices.ObjectBuilder2.dll e Microsoft.Practices.Unity.dll que se encontram na pasta que você "instalou" o Unity, como pode ser visto na figura abaixo:As classes acima são bem simples, no final das contas o que faremos é com que o Unity crie um EnviadorDeEmails com a dependência de ILogger injetada e resolvida, ou seja, que ele crie um EnviadorDeEmails passando para ele um SqlLogger. Para isso vamos "ensinar" ao Unity como resolver a interface ILogger, como pode ser visto no código abaixo:
var unityContainer = new UnityContainer();
unityContainer.RegisterType<ILogger, SqlLogger>();
Na linha 1 criamos uma instância do contêiner do Unity. Na linha 2 dizemos para o Unity que quando quisermos o tipo ILogger (interface) ele deve utilizar a classe concreta SqlLogger. Simples assim.
Agora podemos mandar que o Unity construa nosso EnviadorDeEmails usando constructor injection, conforme visto abaixo:
[TestMethod]
public void Configurando_Unity_Para_Resolver_ILogger(){
var unityContainer = new UnityContainer();
unityContainer.RegisterType<ILogger, SqlLogger>();
var enviadorEmails = unityContainer.Resolve<enviadordeemails>();
Assert.IsInstanceOfType(enviadorEmails.Logger, typeof(SqlLogger));
}
</enviadordeemails>
O grande segredo aí está na linha 7 onde dizemos para o Unity construir nosso EnviadorDeEmails. O Unity percebe que existe uma dependência no construtor do EnviadorDeEmails, e baseado na configuração que fizemos ele sabe como resolver esta dependência. Na linha 9 apenas verificamos se de fato o ILogger utilizado é o SqlLogger, e executando o teste obtemos sucesso.E notem que neste caso utilizamos o constructor injection pois a classe EnviadorDeEmails possui um construtor com uma dependência para uma interface, que o Unity conhece.
Poderíamos dizer que a dependência não deve ser resolvida via construtor, mas sim diretamente na propriedade, para isso alteraríamos a classe EnviadorDeEmails assim:
public class EnviadorDeEmails{ [Dependency]
public ILogger Logger { get;
set;
}
public void EnviarEmail(string email, string mensagem) { //Envia email //registra envio this.Logger.RegistrarMensagem(string.Format("Email enviado para {
}
", email));
}
}
A única diferença aqui foi a utilização do DependencyAttribute na linha 3 para marcar que a propriedade Logger, do tipo ILogger, deve ser resolvida pelo Unity.Executando nosso teste mais uma vez devemos obter sucesso.
A outra forma que o Unity tem para injetar nossas dependências é através da chamada de um método. Por exemplo, imaginem que temos um método Initialize na nossa classe, que é responsável por criar os objetos que nossa classe precisa. Podemos fazer com que o Unity execute este método resolvendo todas as dependências.Vejamos o código da classe EnviadorDeEmails utilizando um Method Call Injection:
public class EnviadorDeEmails{
public ILogger Logger { get;
set;
}
public void EnviarEmail(string email, string mensagem) { //Envia email //registra envio this.Logger.RegistrarMensagem(string.Format("Email enviado para {
}
", email));
}
[InjectionMethod]
public void Inicializador(ILogger logger) { this.Logger = logger;
}
}
Tudo o que fizemos desta vez foi criar um método, neste caso o método Inicializador, na linha 13, que recebe como parâmetros as dependências da nossa classe. E marcamos este método com o InjectionMethodAttribute, para dizer ao Unity que este método deve ser chamado e resolvidor por ele na criação de nosso EnviadorDeEmails.E novamente se executarmos o mesmo método de teste, obteremos sucesso.Como vimos nos três exemplos acima, o Unity após configurado consegue resolver as dependências de nossas classes de forma simples e trivial. Basta alterarmos a forma de resolução da dependência, por exemplo de constructor para setter, e nada no código mudará, assim como se mudar de SqlLogger para XmlLogger, nada no código mudará, apenas a configuração do Unity.Bom galera, é mais ou menos isso. O Unity é uma ferramenta bastante poderosa, extensível e simples de usar.Qualquer dúvida é só escrever nos comentários ou enviar email.
Abraços, Vinicius Quaiato.