簡體   English   中英

C# - Ninject、IoC 和工廠模式

[英]C# - Ninject, IoC and factory pattern

我有一個控制台應用程序,我需要根據用戶的輸入執行某個功能。 如果用戶輸入“功能 1” -> 我執行功能 1,依此類推。

我正在嘗試盡可能干凈和通用地編寫這個項目,並且我想使用IoCSOLID概念,但我有點卡住了。

到目前為止我所擁有的:

public interface IFeature
{
    String execFeature();
}

interface IFeatureFactory
{
    IFeature createFeature(String input);
}

我的第一個想法是在具體的 Factory 類上設置一個關於用戶輸入的switch case ,並相應地創建具體的Feature ,但我敢打賭有更好的方法來使用IoC

我閱讀了 Ninject 工廠擴展,但不明白如何在我的項目中使用它。

使用 IoC/Ninject 進行工廠模式的最佳方法是什么?

如果您的 IFeature 實現沒有其他依賴項,則使用您的方法很好且非常簡單。
例如,假設您有 2 個 IFeature 實現 - SomeFeature 和 OtherFeature ,它們都具有無參數構造函數。
您建議的工廠實現將是這樣的:

public class FeatureFactory: IFeatureFactory
{
    IFeature CreateFeature(string input)
    {
         if(input=="SomeFeature")
         {
           return new SomeFeature();
         }
         else
         {
           return new OtherFeature ();
         }
    }
}

但是,當您的 IFeature 實現使用這種方法有自己的依賴項時,您就失去了使用 Ninject 和 IoC 的意義。
例如,假設 SomeFeature 看起來像這樣:

public class SomeFeature : IFeature
{
    private readonly IDependency1 _dependency1;
    private readonly IDependency2 _dependency2; 

    public SomeFeature (IDependency1 dependency1, IDependency2 dependency2)
    {
        _dependency1=dependency1;
        _dependency2=dependency2;
    }

    string execFeature()
    {
      //Some code here...
    }
}

和OtherFeature類似...

 public class OtherFeature: IFeature
    {
        private readonly IDependency1 _dependency1;
        private readonly IDependency2 _dependency2; 

        public OtherFeature(IDependency1 dependency1, IDependency2 dependency2)
        {
            _dependency1=dependency1;
            _dependency2=dependency2;
        }

        string execFeature()
        {
          //Some code here...
        }
    }

現在你的工廠會變成這樣:

 public class FeatureFactory: IFeatureFactory 
    {
        IFeature CreateFeature(string input)
        {
             if(input=="SomeFeature")
             {
               return new SomeFeature(new Dependency1Implementation(), new Dependency2Implementation());
             }
             else
             {
               return new OtherFeature(new Dependency1Implementation(), new Dependency2Implementation());
             }
        }
    }

在這里,您可以使用 ninject.extensions.factory 的強大功能,通過使用容器為您解決這些依賴項。(這些依賴項可以有自己的依賴項,並且很快就會變得混亂)。
正如其他人提到的,您可以使用命名綁定來綁定每個 IFeature 實現。

Bind<IFeature>().To<SomeFeature>().Named("SomeFeature");
Bind<IFeature>().To<OtherFeature>().Named("OtherFeature");

當然,您還應該綁定其他依賴項

Bind<IDependency1>().To<Dependency1Implementation>();
Bind<IDependency2>().To<Dependency2Implementation>();

然后使用工廠擴展將 IFeatureFactory 綁定到 Factory。

Bind<IFeatureFactory>().ToFactory();

您需要做的是為 IFeatureFactory 中的每個 IFeature 實現創建工廠方法,並根據名為 binding 的 Feature 將其稱為 Get...。

public interface IFeatureFactory
{
  IFeature GetSomeFeature();
  IFeature GetOtherFeature();
}

現在 ninject 會為你實現(!)這個類,並且知道為每個方法選擇哪個實現。(不需要服務定位器......)
您可以在客戶端的輸入上使用 switch 語句來選擇要調用的工廠方法,或者您可以將其包裝在某個包含 switch 語句的提供程序類中,在這兩種情況下,您都不必為IFeature 實現自己。
當然,如果需要和其他更復雜的事情,您可以通過工廠方法將參數傳遞給實現構造函數。

我建議您閱讀本文以獲取更多信息。

編輯
我想強調您不必為每個實現編寫工廠方法,您可以對所有實現使用相同的方法(這是可能的,但更復雜)。
為此,您需要創建自定義實例提供程序來檢測要實例化的實現(例如,根據工廠參數),更多信息請參見上面的鏈接和此處

您可以使用Named Bindings 示例代碼:

Bind<IFeature>().To<Feature1>().Named("Feature1");
Bind<IFeature>().To<Feature2>().Named("Feature2");

欲了解更多信息

編輯

如果您不喜歡Service locator pattern ,上述方法不適合您的情況,因為您必須使用IKernel來解析IFeature

IoC 的主要思想之一是您的解決方案的組件之間不應存在依賴關系。 所以這是很好的做法只使用接口和沒有創建關鍵字“新”類的新實例。 您的問題無法以簡單而優雅的方式解決,因為您只能注入所有功能實現的接口。

所以你有一些特性和它們的實現:

internal interface IFeature
{
}

internal interface IFeature1 : IFeature
{
}

internal interface IFeature2 : IFeature
{
}

還有一個工廠:

internal interface IFeatureFactory
{
    IFeature GetInstance(string featureName);
}

internal class FeatureFactory : IFeatureFactory
{
    private readonly ITypeFactory<IFeature1> feature1;
    private readonly ITypeFactory<IFeature1> feature2;

    private readonly Dictionary<string, ITypeFactory<IFeature>> featuresContainer;

    public FeatureFactory(ITypeFactory<IFeature1> feature1, ITypeFactory<IFeature1> feature2)
    {
        this.feature1 = feature1;
        this.feature2 = feature2;

        featuresContainer = new Dictionary<string, ITypeFactory<IFeature>>
        {
            {"Feature1", feature1},
            {"Feature2", feature1}
        };
    }

    public IFeature GetInstance(string featureName)
    {
        if (!featuresContainer.ContainsKey(featureName))
            throw new Exception(string.Format("Can't create feature {0}", featureName));

        return featuresContainer[featureName].Create();
    }
}

你可以用這種方式注入所有這些東西:

        Bind<IFeatureFactory>().To<FeatureFactory>().InSingletonScope();
        Bind<IFeature1>().To<Feature1>();
        Bind<IFeature2>().To<Feature2>();
        Bind<ITypeFactory<IFeature1>>().ToFactory();
        Bind<ITypeFactory<IFeature2>>().ToFactory();

主要思想是您只有一個用於應用程序的特征工廠實例,並且您存儲特征的注入工廠。 因此,當您第一次訪問 IFeatureFactory 時,Ninject 將創建它的單例實例。 但是您的功能實例只會在您調用 GetInstance() 方法時創建。

要使此代碼工作,您應該添加新接口:

public interface ITypeFactory<out T>
{
    T Create();
}

並安裝 NuGet 包: https : //www.nuget.org/packages/Ninject.Extensions.Factory/

我必須對 Ninject、IoC 和工廠模式應用以下方法。

步驟 1:添加綁定到 IOC 容器

Bind<IFeature>().To<SomeFeature>().Named(nameof(SomeFeature));
Bind<IFeature>().To<SomeFeature>().Named(nameof(SomeFeature));

第二步:創建一個擴展方法來解決你的依賴

public class Resolver
{
   [Inject]
   public IKernal kernal { get; set; }
   public T Resolve<T>(string type)
   {
        return kernal.TryGet<T>(type);
   }
}

第 3 步創建類的實例

IFeature feature = null;
switch (typeOfFeature)
{
    case Feature.SomeFeature:
        feature = Resolver.TryResolve<IFeature>(nameof(SomeFeature));
        break;
    case Feature.OtherFeature:
        feature = Resolver.TryResolve<IFeature>(nameof(OtherFeature));
        break;                
    default:
        throw new Exception("Type not supported");

}
return feature;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM