2013-08-21 13 views
15

jestem przyzwyczajony do tworzenia własnych fabryk, jak pokazano (to jest uproszczone dla ilustracji):Wymień fabrykę z AutoFac

public class ElementFactory 
{ 
    public IElement Create(IHtml dom) 
    { 
     switch (dom.ElementType) 
     { 
      case "table": 
       return new TableElement(dom); 
      case "div": 
       return new DivElement(dom); 
      case "span": 
       return new SpanElement(dom); 
     } 
     return new PassthroughElement(dom); 
    } 
} 

ja wreszcie poruszania się przy użyciu kontenera IoC (AutoFac) w moim prądzie projekt, i zastanawiam się, czy istnieje jakiś magiczny sposób osiągnięcia tego samego elegancko dzięki AutoFac?

+1

Jaką korzyść stanowi zastąpienie tej fabryki Autofac? – Steven

+0

Przepraszam, jak wspomniałem w moim komentarzu do odpowiedzi KeithS, miałem nadzieję, że będzie jakiś voodoo AutoFac, który pozwoli mi uniknąć zmiany. Jaka jest zaleta unikania przełącznika? Nie wiem ... Wydaje mi się, że byłem indoktrynowany niechęcią do nich :( –

+1

IoC to żadna magiczna różdżka.Możesz użyć nazwanych rejestracji i możesz usunąć przełącznik, ale na końcu zawsze będzie jakiś rodzaj logika przełączania Moja rada: użyj fabryki i zarejestruj fabrykę w Autofac – Steven

Odpowiedz

23

Krótka odpowiedź: tak.

Dłuższa odpowiedź: po pierwsze, w prostych przypadkach, w których klasa Foo jest zarejestrowana jako implementacja dla IFoo, parametry konstruktora lub właściwości typu Func<IFoo> zostaną automatycznie rozwiązane przez Autofac, bez potrzeby dodatkowego okablowania. Autofac wstrzyknie delegata, który w zasadzie wywoła container.Resolve<IFoo>() po wywołaniu.

W bardziej złożonych przypadkach, takich jak twój, gdzie dokładne zwrócenie konkrecji opiera się na parametrach wejściowych, można wykonać jedną z dwóch czynności. Po pierwsze, można zarejestrować metody fabryki jako jego wartość powrót do zapewnienia parametryczne uchwałę:

builder.Register<IElement>((c, p) => { 
    var dom= p.Named<IHtml>("dom"); 
    switch (dom.ElementType) 
    { 
     case "table": 
      return new TableElement(dom); 
     case "div": 
      return new DivElement(dom); 
     case "span": 
      return new SpanElement(dom); 
    } 
    return new PassthroughElement(dom); 
    }); 

//usage 
container.Resolve<IElement>(new NamedParameter("dom", domInstance)) 

To nie jest typ-safe (domInstance kompilator nie będzie sprawdzane, aby upewnić się, że jest to IHtml), ani bardzo czysty. Zamiast Innym rozwiązaniem jest rzeczywiście zarejestrować metody fabryki jako Func:

builder.Register<Func<IHtml, IElement>>(dom => 
{ 
    switch (dom.ElementType) 
    { 
     case "table": 
      return new TableElement(dom); 
     case "div": 
      return new DivElement(dom); 
     case "span": 
      return new SpanElement(dom); 
    } 
    return new PassthroughElement(dom); 
}); 


public class NeedsAnElementFactory //also registered in AutoFac 
{ 
    protected Func<IHtml,IElement> CreateElement {get; private set;} 

    //AutoFac will constructor-inject the Func you registered 
    //whenever this class is resolved. 
    public NeedsAnElementFactory(Func<IHtml,IElement> elementFactory) 
    { 
     CreateElement = elementFactory; 
    } 

    public void MethodUsingElementFactory() 
    { 
     IHtml domInstance = GetTheDOM(); 

     var element = CreateElement(domInstance); 

     //the next line won't compile; 
     //the factory method is strongly typed to IHtml 
     var element2 = CreateElement("foo"); 
    } 
} 

Jeśli chcesz zachować kod w ElementFactory zamiast wprowadzenie go w module Autofac, można dokonać metodą fabryczną statyczne i zarejestrować się, że (sprawdza się to szczególnie w twoim przypadku, ponieważ twoja metoda fabryczna jest statycznie statyczna):

public class ElementFactory 
{ 
    public static IElement Create(IHtml dom) 
    { 
     switch (dom.ElementType) 
     { 
      case "table": 
       return new TableElement(dom); 
      case "div": 
       return new DivElement(dom); 
      case "span": 
       return new SpanElement(dom); 
     } 
     return new PassthroughElement(dom); 
    } 
} 

... 

builder.Register<Func<IHtml, IElement>>(ElementFactory.Create); 
+1

Dzięki, to naprawdę przydatne! Miałem trochę nadziei, że AutoFac pomoże mi uniknąć brzydkiego oświadczenia przełącznika, ale to było, patrząc na mnie, praktycznie kpiąc ze mnie, we wszystkich trzech blokach kodu :) –

+2

Możliwe jest również użycie "rejestracji z kluczem", określając nazwy każdego typu elementu jako klucz do metody rozwiązywania dla odpowiedniego typu obiektu. Powikłanie w tej sytuacji polega na tym, że nie wszystkie możliwe typy zwracania są powiązane z kluczem (masz domyślny przypadek), a rodzic macierzysty IHtml nazwy elementu jest potrzebny z innych powodów. Wydawało się więc, że najprościej jest po prostu wziąć swój działający kod i ustawić go tak, aby AutoFac mógł z niego korzystać. – KeithS

+2

Rejestracja nie jest kompilowana: 'builder.Register > (ElementFactory.Create);' Spodziewam się, że będzie wyglądać mniej więcej tak: 'builder.Register > ((kontekst, parametry) => (html => ElementFactory.Create (html))); ' –