Injeção de dependência minimalista

Simplificando seu caminho

Se tudo o que você precisa é uma simples e eficiente forma de realizar Injeção de dependência e inversão de controle, abaixo um código minúsculo que pode te ajudar a começar seu dia mais facilmente.

O problema

Em .Net, geralmente para que seja possível utilizar Inversão de Controle e/ou Injeção de dependência nós programadores acabamos recorrendo a frameworks externos. Alguns bons exemplos destes frameworks são os ótimos Unity e Ninject.

Porém, existem os casos onde nossas dependências podem ser gerenciadas de forma simplificada e tudo o que precisamos é de um resolver.

To the code

public abstract class InstanceResolverFor<SomeType>
{
    public static Func<SomeType> InstanceBuilder = () =>
    {
        throw new Exception(string.Format("The type '{0}' does not have a valid factory.", typeof(SomeType).FullName));
    };

    public static SomeType Instance { get { return InstanceBuilder(); } }
}

Utilização

[TestMethod]
public void Get_Instance_From_Valid_Object()
{
    InstanceResolverFor<ISomeInterface>.InstanceBuilder =
        () => new InterfaceImplementation();

    var instance = InstanceResolverFor<ISomeInterface>.Instance;
    Assert.IsNotNull(instance);
}

[TestMethod]
public void Get_Instance_From_Invalid_Object()
{
    try
    {
        var a = InstanceResolverFor<object>.Instance;
        Assert.Fail("Object should not have being returned.", a);
   }
    catch { }
}

Como isso funciona?

Quando você realiza o registro da criação da instância na classe instance resolver (conforme snipet abaixo), você está na realidade instruindo o compilador a emitir uma nova classe com base em nosso template genério.

InstanceResolverFor<IDoSomething>.InstanceBuilder = () => new DoSomething();

Para se ter uma ideia das diferenças, uma eventual chamada para obter a implementação de uma interface que não possui nenhum desvio registrado resultaria no seguinte código compilado.

call       !0 class ConsoleApplication1.Program/InstanceResolverFor`1<class ConsoleApplication1.Program/IDoSomething>::get_Instance()

Desta forma, esta chamada iria diretamente ao ponto onde uma exception é disparada informando sobre a inexistência deste desvio.

Após o registro do desvio para nossa implementação, o código se transforma bastante. Primeiro, ganhamos um novo método Main que conterá o delegate de desvio:

.method private hidebysig static class ConsoleApplication1.Program/IDoSomething 
 '<Main>b__0'() cil managed
{
 .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
 // Code size 10 (0xa)
 .maxstack 1
 .locals init ([0] class ConsoleApplication1.Program/IDoSomething CS$1$0000)
 IL_0000: newobj instance void ConsoleApplication1.Program/DoSomething::.ctor()
 IL_0005: stloc.0
 IL_0006: br.s IL_0008
 IL_0008: ldloc.0
 IL_0009: ret
} // end of method Program::'<Main>b__0'

Depois, ganhamos também uma classe para controlar a execução deste vínculo:

.field private static class [mscorlib]System.Func`1<class ConsoleApplication1.Program/IDoSomething> 'CS$<>9__CachedAnonymousMethodDelegate1'
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 

E, finalmente, nosso método main agora inicializa obtendo uma instância desta nova classe e a utiliza para realizar a chamada de obtenção da instância que queremos.

...
IL_0001: ldsfld class [mscorlib]System.Func`1<class ConsoleApplication1.Program/IDoSomething> ConsoleApplication1.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
 ...
IL_0009: ldftn class ConsoleApplication1.Program/IDoSomething ConsoleApplication1.Program::'<Main>b__0'()
IL_000f: newobj instance void class [mscorlib]System.Func`1<class ConsoleApplication1.Program/IDoSomething>::.ctor(object,
 native int)
 IL_0014: stsfld class [mscorlib]System.Func`1<class ConsoleApplication1.Program/IDoSomething> ConsoleApplication1.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
 IL_0019: br.s IL_001b
 IL_001b: ldsfld class [mscorlib]System.Func`1<class ConsoleApplication1.Program/IDoSomething> ConsoleApplication1.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
 IL_0020: stsfld class [mscorlib]System.Func`1<!0> class ConsoleApplication1.Program/InstanceResolverFor`1<class ConsoleApplication1.Program/IDoSomething>::InstanceBuilder
 IL_0025: call !0 class ConsoleApplication1.Program/InstanceResolverFor`1<class ConsoleApplication1.Program/IDoSomething>::get_Instance()
 ...

Bônus

Você pode, no momento do registro do desvio/criação da instância, decidir por exemplo que deseja expor sua referência como Singleton. Neste caso, acho que vale a pena dar uma olhada neste post!

Extra

Para servir de contraponto ao código que acabei de fornecer, aqui vai um link para o post de um cara que defende que isto seria um anti-pattern. Fica a seu critério decidir o que acha.

Um comentário

  1. Pingback: Simple Singleton | Felipe G Te . com

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: