2013-08-21 58 views
15

我習慣於如圖創造我自己的工廠(這是簡化的圖示):替換工廠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); 
    } 
} 

我終於讓周圍,以我目前使用IoC容器(AutoFac)項目,我想知道是否有一些神奇的方式與AutoFac優雅地實現相同的事情?

+1

用Autofac替換這個工廠的優勢是什麼? – Steven

+0

對不起,正如我在我對KeithS的回答的評論中提到的那樣,我希望會有一些AutoFac voodoo讓我避免切換。避免交換機的優點是什麼?我不知道......我似乎已經厭惡他們:( –

+1

IoC不是魔術棒,你可以使用命名註冊,並且你可以移除開關,但最終總會有某種形式的開關邏輯,我的建議是:使用工廠堡壘,並在Autofac註冊工廠 – Steven

回答

23

簡答:是的。

較長答案:首先,在將類Foo被登記爲IFoo的實施簡單的情況下,構造函數的參數或Func<IFoo>類型的屬性將自動被Autofac解決,無需額外的佈線。 Autofac將注入一個委託,在調用時基本執行container.Resolve<IFoo>()

在像您這樣的複雜情況下,返回的確切結果基於輸入參數,您可以執行以下兩項操作之一。首先,你可以註冊一個工廠方法,它的返回值提供參數分辨率:

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)) 

這不是類型安全(domInstance不會被編譯器檢查,以確保它是一個IHTML),也不是很清潔。相反,另一種解決方案是實際註冊的工廠方法爲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"); 
    } 
} 

如果你想保持在ElementFactory而不是把它Autofac模塊中的代碼,你可以使工廠方法靜態和寄存器(這在你的情況下效果特別好,因爲你的工廠方法是平凡的靜態):

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

謝謝,這真的很有用!我希望AutoFac能夠幫助我避免醜陋的開關語句,但實際上它正在凝視着我嘲笑我,在所有的三個代碼塊:) –

+2

那麼它也可以使用「鍵控註冊」,指定每個元素類型名稱作爲相應對象類型的解決方法的關鍵。在這種情況下的複雜情況是並非所有可能的返回類型都與一個關鍵字相關聯(您有一個默認情況),並且出於其他原因需要元素名稱的IHtml父項。所以,把你的工作代碼設置好,AutoFac就可以使用它,這似乎是最簡單的。 – KeithS

+2

註冊不會編譯: 'builder.Register >(ElementFactory.Create);' 我期望它看起來有點像這樣: 'builder.Register >((context,parameters)=>(html => ElementFactory.Create(html)));' –