Inversão de controle com Unity
Sexta-feira, 18 de julho de 2008 por CarlosNo último post iniciamos nossa breve visita ao “Enterprise Library 4.0” com o “Cryptography Application Block”. Neste post vamos analisar o “Unity Application Block” que é uma novidade na versão 4 da biblioteca. O “Unity” é um container de inversão de controle baseado na biblioteca “ObjectBuilder2” do “Entreprise Library” e que foi lançado como uma alternativa às já tradicionais bibliotecas “Spring.NET” (uma versão da famosa biblioteca Spring do mundo Java), “Castle Windsor”, “StructureMap”, “ObjectBuilder” etc.
Quem já utilizava o “Enterprise Library” em alguma de suas versões anteriores deve estar familiarizado com a biblioteca “ObjectBuilder”. Ela atuava no núcleo do “Enterprise Library” cuidando do gerenciamento dos objetos e apesar de não ser um “cidadão de primeira classe” (já que não era promovido como um dos conjuntos de aplicação), podia ser referenciado e utilizado diretamente pelo usuário. Muitos reclamavam de questões relativas ao design e performance da biblioteca e, a partir do feedback dos usuários e observação dos projetos da comunidade, a Microsoft refez o código e lançou o “ObjectBuilder2”. Baseado nele, foi construído o “Unity Application Block” e a novidade é que apesar de fazer parte do “Enterprise Library”, pode ser baixado separadamente através do portal CodePlex.
Conforme mencionei, o “Unity” é apenas uma alternativa a outras bibliotecas. Dito isto, podemos esperar muitas funcionalidades parecidas, algumas inéditas e algumas que faltam. A intenção deste post não é fazer uma comparação entre todas as bibliotecas disponíveis, mas visitar algumas das funcionalidades básicas do “Unity” para que seja mais fácil explorar daí pra frente.
Usando o “Unity Application Block”
Após baixar e instalar o pacote do CodePlex, vamos criar um novo ConsoleApplication no Visual Studio 2008 e referenciar as duas bibliotecas principais do “Unity” que já estão registradas no GAC da máquina: Microsoft.Practices.Unity, Microsoft.Practices.Unity.Configuration e Microsoft.Practices.Unity.ObjectBuilder2.
Para iniciarmos com um exemplo, vamos imaginar uma aplicação simples onde queremos criar um log. Podemos ter várias implementações deste “logger”, por exemplo, uma que escreve as mensagens direto no console e outra que escreve em um arquivo texto. Se fizermos com que estes dois “loggers” implementem uma interface comum, ganhamos flexibilidade por podermos especificar através de um arquivo de configuração qual gostaríamos de usar.
Esta seria a interface em questão:
public interface ILogger
{
void Log(string message);
}
Há apenas um método que recebe a mensagem que gostaríamos de escrever. Para ela, criamos duas implementações:
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
}
public class TextFileLogger : ILogger
{
public void Log(string message)
{
using (StreamWriter streamWriter =
new StreamWriter("log.txt", true))
{
streamWriter.WriteLine(message);
}
}
}
Se utilizarmos apenas a interface ILogger, já podemos escrever o comportamento da nossa prova de conceito:
ILogger logger;
logger.Log("Aplicação foi iniciada.");
logger.Log("Aplicação foi finalizada.");
Falta apenas inicializar o objeto logger. Para isso, utilizaremos as classes do namespace Microsoft.Practices.Unity, de forma que o método Main fica assim:
static void Main(string[] args)
{
UnityContainer unityContainer = new UnityContainer();
unityContainer.RegisterType(typeof (ILogger),
typeof (ConsoleLogger));
ILogger logger = unityContainer.Resolve<ILogger>();
logger.Log("A aplicação foi iniciada.");
Console.ReadLine();
logger.Log("A aplicação foi finalizada.");
}
Vamos analisar passo-a-passo o que fizemos: em primeiro lugar, inicializamos o objeto unityContainer com o construtor padrão. Depois, configuramos programaticamete o Unity através do método RegisterType. Neste caso, criamos a associação dizendo que a interface ILogger deve ser associada à implementação ConsoleLogger. Ao rodar o programa, podemos ver que as mensagens são impressas na tela. Seriam impressas no arquivo log.txt se a associação da interface ILogger fosse com a implementação TextFileLogger.
A princípio, a menos que a intenção seja aproveitar o controle do ciclo de vida dos objetos que o Unity fornece (para encapsular nossa implemenação em um singleton, ou pedir uma instância por chamada), não há muito sentido em configurá-lo programaticamente. O código equivalente a este, mas com as configurações no App.config seria o seguinte:
static void Main(string[] args)
{
UnityContainer unityContainer = new UnityContainer();
UnityConfigurationSection unityConfigurationSection =
(UnityConfigurationSection) ConfigurationManager.GetSection(”unity”);
unityConfigurationSection.Containers.Default.Configure(unityContainer);
ILogger logger = unityContainer.Resolve<ILogger>();
logger.Log("A aplicação foi iniciada.");
Console.ReadLine();
logger.Log("A aplicação foi finalizada.");
}
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="unity" type= "Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" /> </configSections>
<unity> <typeAliases> <typeAlias alias="ILogger" type="ConsoleApplication1.ILogger, ConsoleApplication1" /> <typeAlias alias="ConsoleLogger" type="ConsoleApplication1.ConsoleLogger, ConsoleApplication1" /> <typeAlias alias="TextFileLogger" type="ConsoleApplication1.TextFileLogger, ConsoleApplication1" /> </typeAliases> <containers> <container> <types> <type type="ILogger" mapTo="ConsoleLogger" /> </types> </container> </containers> </unity> </configuration>
Com este exemplo iniciamos a utilização do Unity, mas ainda há muitos recursos a explorar. Na seção de links, ao final do post, há uma lista de blogs que possuem exemplos de aplicação que estendem o que já fizemos. Uma modificação imediata que poderíamos fazer seria especificar o nome do arquivo log.txt através do próprio arquivo App.config, bastando apenas passá-lo através de um construtor da classe ConsoleLogger.













