2

我已閱讀了很多關於此主題的內容,但無法完全掌握它。C# - 使用依賴注入(ninject)而不是工廠模式

我正在嘗試使用Ninject.Extensions.Factory而不是我的工廠根據用戶輸入創建新對象。我想充分利用Ninject功能和IoC概念。

現在的代碼如下所示:

interface IFeatureFactory 
{ 
    IFeature createFeature(int input); 
} 

和:

class BasicFeatureFactory : IFeatureFactory 
{ 
    public IFeature createFeature(int input) 
    { 
     switch (input) 
     { 
      case 1: 
       return new FirstFeature(); 
      case 2: 
       return new SecondFeature(); 
      default: 
       return null; 
     } 
    } 
} 

在未來,的iFeature將有依賴,所以我想這樣做的IoC的方式。

編輯:

消費類 - 在IFeatureFactoryIUILayer注入FeatureService構造和使用Ninject解決。

private IFeatureFactory featureFactory; 
    private IUILayer uiHandler; 

    public FeatureService(IFeatureFactory featureFactory, IUILayer uiHandler) 
    { 
     this.featureFactory = featureFactory; 
     this.uiHandler = uiHandler; 
    } 

    public void startService() 
    { 
     int userSelection = 0; 
     uiHandler.displayMenu(); 
     userSelection = uiHandler.getSelection(); 
     while (userSelection != 5) 
     { 
      IFeature feature = featureFactory.createFeature(userSelection); 
      if (feature != null) 
      { 
       IResult result = feature.execFeature(); 
       uiHandler.displayResult(result); 
      } 
      else 
      { 
       uiHandler.displayErrorMessage(); 
      } 
      uiHandler.displayMenu(); 
      userSelection = uiHandler.getSelection(); 
     } 
    } 

和iFeature的類:

public interface IFeature 
{ 
    IResult execFeature(); 
} 

綁定:

public override void Load() 
    { 
     Bind<IFeatureFactory>().To<BasicFeatureFactory>(); 
     Bind<IUILayer>().To<ConsoleUILayer>(); 
    } 

我怎麼可以這樣工廠模式轉換爲的IoCNinject.Extensions.Factory?請記住,創建IFeature取決於用戶輸入。

+0

您能否展示'IFeatureFactory'和'IFeature'的使用者如何使用這些抽象? – Steven

+0

那麼,這個最好的解決方案是什麼?使用Ninject.Extensions.Factory是一個好主意嗎?或者像這樣離開它?我該如何使用Ninject.Extensions.Factory?謝謝。 –

+0

我覺得這個問題對於你想解決什麼問題有點太模糊。你是否真的要求這樣來幫助你面對將來的申請?如果是這樣,如果有10個人回答它,那麼根據大家認爲「正確」的方式來處理未來打樣,你可能會得出12種不同的意見。我能說的最好的事情是,首先需要模擬您的需求**,然後擔心如何使您的工具支持該需求。 –

回答

2

對我來說,看起來你有2個選項來重構你的代碼,以獲得ninject的全部好處。
你現在的工作方式與pure di沒有什麼不同(它沒有錯,它在某些情況下更好),但正如你所說你想充分使用ninject功能。

選擇一個
而是注入IFeatureFactory到FeatureService注入接口IFeatureProvider這將是這樣的:

public interface IFeatureProvider 
{ 
    IFeature GetFeature(int featureType); 
} 

從這個供應商,而不是工廠現在你FeatureService將得到所要求的功能。
你將需要實現IFeatureProvider併爲您將需要2個接口IFirstFeatureFactory和ISecondFeatureFactory:

public interface IFirstFeatureFactory 
{ 
    IFeature CreateFirstFeature(); 
} 

public interface ISecondFeatureFactory 
{ 
    IFeature CreateSecondFeature(); 
} 

現在IFeatureProvider impelementaion:

public class FeatureProvider: IFeatureProvider 
    { 
     private readonly IFirstFeatureFactory _firstFeatureFactory; 
     private readonly ISecondFeatureFactory _secondFeatureFactory; 

     public FeatureProvider(IFirstFeatureFactory firstFeatureFactory, ISecondFeatureFactory secondFeatureFactory) 
     { 
      _firstFeatureFactory=firstFeatureFactory; 
      _secondFeatureFactory=secondFeatureFactory; 
     } 

     public IFeautre GetFeature(int featureType) 
     { 
      switch(featureType) 
      { 
       case 1: 
        return _firstFeatureFactory.CreateFirstFeature(); 
       case 2: 
        return _secondFeatureFactory.CreateSecondFeature(); 
       default: 
        return null; 
      } 
     } 
    } 

的事情,你應該注意到我剛纔解壓負責「新」到另一個界面的對象。
我們不會實現兩個工廠接口,因爲ninject會爲我們做,如果我們將它正確地綁定它。
結合:

Bind<IFeature>().ToFeature<FirstFeature>().NamedLikeFactoryMethod((IFirstFeatureFactory o) => o.CreateFirstFeature()); 
Bind<IFeature>().ToFeature<SecondFeature>().NamedLikeFactoryMethod((ISecondFeatureFactory o) => o.CreateSecondFeature()); 
Bind<IFirstFeatureFactory>().ToFactory(); 
Bind<ISecondFeatureFactory>().ToFactory(); 
Bind<IFeatureProvider>().To<FeatureProivder>(); 

這種「NameLikeFactoryMethod」結合是使用的命名結合像我一樣here等同,現在是the recommended way by ninject for factories

這裏需要注意的是,你自己並沒有實現IFirstFeatureFactory和ISecondFeatureFactory,並且你正在使用ninject的functionallity。

這個選項的主要缺點是當我們需要添加更多的功能時,我們需要創建除了功能本身以外的另一個FeatureFactory,並且還要更改FeatureProvider來處理它。 如果功能不改變往往這個選項可以很好的和簡單,但如果他們這樣做它可以成爲維護的噩夢,這就是爲什麼我建議選擇2

選擇二
在這個選項中,我們不會創建任何提供者類,並將所有創建邏輯放入工廠。
IFeatureFactory接口看起來與您現在使用的接口非常相似,但不是使用int作爲參數,而是使用一個字符串(對於命名的綁定,我們將很快看到它會更好)。

public interface IFeatureFactory 
{ 
    IFeature CreateFeature(string featureName); 
} 

我們不會落實自己這個接口,讓ninject爲我們做它,但是我們需要告訴ninject使用CearteFeature的第一個參數,檢測實例(FirstFeatue或SecondFeature)的實施。
爲此,我們需要使用此行爲的自定義實例提供程序作爲StandardInstanceProvider使用其他約定來選擇要實例化的實現(默認約定in this article)。
幸運的是,ninject顯示了我們如何通過UseFirstArgumentAsNameInstanceProvider快速實現它。
現在結合:

Bind<IFeature>().To<FirstFeature>().Named("FirstFeature"); 
Bind<IFeature>().To<FirstFeature>().Named("SecondFeature"); 
Bind<IFeatureFactory>().ToFactory(() => new UseFirstArgumentAsNameInstanceProvider()); 

這裏要注意的事情:

  • 我們不會自己實現工廠接口。
  • 我們對每個實現都使用了命名綁定,我們將根據我們將傳遞給工廠方法的featureName從工廠獲取實現(這就是爲什麼我更喜歡參數是字符串)。
    請注意,如果您傳遞的功能名稱不是「FirstFeature」或「SecondFeature」,則將拋出異常。
  • 如前所述,我們正在使用UseFirstArgumentAsNameInstanceProvider作爲我們工廠的實例提供程序。

這已經解決了第一個選項中的問題,就好像我們想添加新功能一樣,我們只需要創建它並將其與他的界面綁定到他的名字。
現在工廠的客戶可以從工廠功能中請求這個新名稱,而無需在其他類中進行更改。

Conslucsion
通過選擇該選項之一以上與純二,我們將得到讓ninject內核的好處在工廠而不是做創建我們的對象都在我們自己的「新」。
儘管沒有使用IoC容器沒有任何問題,但如果ninject會爲我們注入這些內容,那麼在IFeature實現中存在大的依賴關係圖時,這確實可以幫助我們。 通過執行此選項之一,我們完全使用ninject功能而不使用被稱爲反模式的「服務定位器」。

+0

感謝這個非常詳細的答案。我最終使用了第二個選項,就像你所建議的那樣,但我遇到的一個問題是 - 可以說我有基於輸入「1」和「2」的FirstFeature和SecondFeature的實現。比用戶輸入「3」 - 如何處理這個選項返回null?因爲現在應用程序崩潰說「沒有匹配的綁定可用,並且類型不可自行綁定」。再次感謝 –

+0

我的意思是,處理這個問題的唯一方法是在獲得IFeature之前檢查輸入?或者ninject有辦法處理這類問題。謝謝 –

+0

我幾乎可以肯定,如果你重寫另一個ninject deafult行爲,可能會以某種方式。但在我看來,你不應該這樣做,最好檢查輸入或在try catch塊中將調用包裝到工廠中。 – YuvShap

1

您可以將這些功能注入BasicFeatureFactory的構造函數中。

class BasicFeatureFactory : IFeatureFactory 
{ 
    FirstFeature feature1; 
    SecondFeature feature2; 

    public BasicFeatureFactory(FirstFeature feature1, SecondFeature feature2) { 
     this.feature1 = feature1; 
     this.feature2 = feature2; 
    } 

    public IFeature createFeature(int input) { 
     switch (input) { 
      case 1: return this.feature1; 
      case 2: return this.feature2; 
      default: return null; 
     } 
    } 
} 
+0

唯一我可能會建議的是將c'tor參數更改爲「IFeature」,並使用Ninject的上下文綁定功能來確定「FirstFeature」和「SecondFeature」是什麼:https://github.com/ninject/Ninject/wiki/Contextual-Binding –

+0

@JoeAmenta:我會說「最簡單的事情可能可行」:)。您也可以注入內核本身並從容器中解析。 – Steven