C# - Ninject、IoC 和工厂模式

C# - Ninject, IoC and factory pattern

我有一个控制台应用程序,我需要在其中根据用户的输入执行特定功能。如果用户输入 "feature 1" -> 我执行功能 1,依此类推。

我正在尝试将此项目编写得尽可能干净和通用,并且我想使用 IoCSOLID 概念,我有点卡住了。

我目前有:

public interface IFeature
{
    String execFeature();
}

interface IFeatureFactory
{
    IFeature createFeature(String input);
}

我的第一个想法是在 concrete Factory class 上有一个关于用户输入的 switch case ,并相应地创建具体的 Feature,但我敢打赌 IoC.

有更好的方法

我阅读了 Ninject 工厂扩展,但不明白如何在我的项目中使用它。

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

您可以使用 Named Bindings。示例代码:

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

更多info

编辑

如果您不喜欢 Service locator pattern,上述方法不适合您的情况,因为您必须使用 IKernel 来解析 IFeature

如果您的 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 绑定到工厂。

Bind<IFeatureFactory>().ToFactory();

您需要做的是在 IFeatureFactory 中为您的每个 IFeature 实现创建工厂方法,并根据名为 binding 的 Feature 调用它 Get...

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

现在 ninject 将为您实现(!)这个 class 并知道为每个方法选择哪个实现。(不需要服务定位器....)
您可以在客户端的输入上使用 switch 语句来选择要调用的工厂方法,或者您可以将其包装在某个包含 switch 语句的提供程序 class 中,在这两种情况下,您都不必执行'new' 自己实现 IFeature。
当然,如果需要和其他更复杂的事情,您可以通过工厂方法将参数传递给实现构造函数。

我建议您阅读 this 以获取更多信息。

编辑
我想强调的是,您不必为每个实现都编写工厂方法,您可以对所有实现使用相同的方法(这是可能的,但更复杂)。
为此,您需要创建自定义实例provider 来检测要实例化哪个实现(例如根据工厂参数),更多信息请参见上面的 link 和 here.

IoC 的主要思想之一是您的解决方案的组件之间不应存在依赖关系。因此,最好只使用接口并且不要使用关键字 "new" 创建 your 类 的新实例。 您的问题无法以简单而优雅的方式解决,因为您只能注入所有功能实现的接口。

所以你有一些功能和它们的实现:

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 和工厂模式应用以下方法。

第一步: 添加了对 IOC 容器的绑定

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

第 2 步: 创建一个扩展方法来解决您的依赖关系

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

第 3 步创建 class

的实例
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;