2011-07-27 283 views
6

用DI方法處理條件子工廠的體面方式是什麼? 該場景是注入到條目中的加載器對象取決於該條目的設置。最初我是將IoC容器注入工廠,並使用它來根據命名約定進行解析。但是,我真的很想保持工廠清潔的容器。依賴注入和工廠

工廠被注入到一個類中,該類將加載數據庫中的所有設置,然後使用工廠創建一組條目。該設置確定在給定條目內使用哪個裝載器。

編輯:更改代碼以更好地突出顯示實際問題。 問題是,必須同時支持多個數據庫管理器,如果情況並非如此,那麼它會很簡單。數據庫管理器類型由爲特定條目存儲的條目設置確定。

public class Entry : IEntry 
{ 
    private ISomething loader; 

    public Entry(ISomething something) 
    { 
     this.loader = something; 
    } 
} 

public class EntryFactory : IEntryFactory 
{ 
    IEntry BuildEntry(IEntrySetting setting) 
    { 
     //Use setting object to determine which database manager will be used 
    } 
} 

public class EntryManager 
{ 
    public EntryManager(IEntryFactory entryFactory) 
    { 
     var entrySettings = this.settings.Load(); 
     foreach(var setting in entrySettings) 
     { 
      this.entries.Add(entryFactory.BuildEntry(setting)); 
     } 
    } 
} 

我曾考慮具有子工廠,主要工廠註冊和解析他們的方式,但我不知道是否有更好的方法。

+1

您有一個運行時輸入到依賴關係解析機制,所以我懷疑有一種方法來實現完整的容器獨立性。所以把它推到工廠似乎是你能做的最好的。您也可能會發現[Autofac的關係類型](http://code.google.com/p/autofac/wiki/RelationshipTypes)有趣。 –

+0

正如我看到你正在你的入門構造函數中工作,這就是爲什麼整個DI和工廠人員不工作的問題。 loader.Load(); 讓構造函數只包含字段初始化。加載方法是做什麼的?它是否用數據填充入口對象?在這種情況下,這不是一個構造函數。工廠必須準備好將加載的數據用於輸入,從其他地方獲取。 –

回答

0

這取決於你的DI框架允許和你沒有指定它。使用Autofac基於委託的註冊,我來到以下解決方案。請注意,在這兩種情況下,ILoaderFactoryIEntryFactory已被替換爲簡單的Func<>工廠。

解決方案1,用兩個工廠:

public class EntryManager 
{ 
    public EntryManager(Func<ILoader, IEntry> entryFactory, Func<Settings, ILoader> loaderFactory) 
    { 
     var entrySettings = this.settings.Load(); 
     foreach(var setting in entrySettings) 
     { 
      this.entries.Add(entryFactory(loaderFactory(setting))); 
     } 
    } 
} 

private static ILoader SelectLoader(IEntrySetting settings) 
{ 
    // your custom loader selection logic 
} 

var builder = new ContainerBuilder(); 
builder.RegisterType<EntryManager>(); 
builder.RegisterType<Entry>().As<IEntry>(); 
builder.Register((c, p) => SelectLoader(p.TypedAs<IEntrySetting>())); 
IContainer container = builder.Build(); 
container.Resolve<EntryManager>(); 

方案2中,僅使用一個工廠:

public class EntryManager 
{ 
    public EntryManager(Func<IEntrySetting, IEntry> entryFactory) 
    { 
     var entrySettings = this.settings.Load(); 
     foreach(var setting in entrySettings) 
     { 
      this.entries.Add(entryFactory(setting)); 
     } 
    } 
} 

private static ILoader SelectLoader(IEntrySetting settings) 
{ 
    // your custom loader selection logic 
} 

var builder = new ContainerBuilder(); 
builder.RegisterType<EntryManager>(); 
builder.Register((c, p) => new Entry(SelectLoader(p.TypedAs<IEntrySetting>()))).As<IEntry>(); 
IContainer container = builder.Build(); 
container.Resolve<EntryManager>(); 
+0

選擇邏輯是我質疑的部分,我使用它來讓容器根據設置通過使用工廠中的已命名註冊解析來解析類型。問題是將工廠與明確瞭解容器的工廠區分開來的好方法是什麼? – crb04c

+0

Unity也支持自動工廠。 – TrueWill

1

我通常做的是創造我的DI容器包裝...像IDependencyResolver ...並注入我的工廠。然後,你可以有一個像StructureMapDependencyResolver這樣的實現。我喜歡這比注入容器本身更好,因爲這使我可以立即更改DI容器(幾乎)。至少我的工廠不需要改變。

public interface IDependencyResolver 
{ 
    T Resolve<T>(); 
} 

public class UnityDependencyResolver : IDependencyResolver 
{ 
    private readonly IUnityContainer _container; 

    public UnityDependencyResolver(IUnityContainer container) 
    { 
     _container = container; 
    } 

    public T Resolve<T>() 
    { 
     return _container.Resolve<T>(); 
    } 
} 

這種方法非常靈活,您可以實現自己的依賴關係解析器並手動注入它們。

public class ManualDependencyResolver : IDependencyResolver 
{ 
    public T Resolve<T>() 
    { 
     if (typeof(T)==typeof(ITransactionRepository)) 
     { 
      return new CheckTransactionRespostory(new DataContext()); 
     } 

     throw new Exception("No dependencies were found for the given type."); 
    } 
} 
+3

這裏的難點在於它引入了一點Service Locator模式..這在工廠中可能很好,但如果我需要/想要並隱藏依賴關係,它確實會使切換到無容器變得更加困難。 – crb04c

+0

好點。我不介意工廠的服務定位器。然而,如果你想完全移除容器,你可以實現自己的依賴解析器,這些解析器具有硬編碼的類依賴關係,甚至可以通過配置文件進行配置。當我鍵入這些內容時,我有點畏縮,但是如果容器導致問題,您在技術上可以做到這一點。 –

+0

傳遞DI容器往往是反模式。容器本身就是一個工廠。你已經減少了使用包裝的痛苦,但我不認爲這是一個最佳的解決方案。 – TrueWill