C# - Ninject,IoC和工厂模式

时间:2016-04-24 13:28:30

标签: c# ninject inversion-of-control ioc-container factory-pattern

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

我正在尝试将此项目编写为尽可能干净且通用,我想使用 IoC SOLID 概念,我有点卡住。< / p>

到目前为止我有什么:

ports:
  - "7777:7777"

public interface IFeature
{
    String execFeature();
}

我的第一个想法就是在具体工厂类上有关于用户输入的切换案例,并创建具体的功能因此,但我打赌用 IoC 来做更好的方法。

我读过有关Ninject工厂扩展的内容,但我并不了解如何在我的项目中使用它。

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

4 个答案:

答案 0 :(得分:2)

如果您的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...
    }
}

和其他特征类似......

 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的功能将其命名为Get ....

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

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

修改
我想强调你不必为每个实现编写工厂方法,你可以使用相同的方法(可能但更复杂)。
要做到这一点你需要创建自定义实例提供程序,用于检测要实例化的实现(例如,根据工厂参数),在上面的链接和here中有关于此的更多信息。

答案 1 :(得分:0)

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

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

了解更多info

修改

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

答案 2 :(得分:0)

IoC的一个主要想法是,您不应该在解决方案的组件之间存在依赖关系。因此,仅使用界面并且不使用关键字&#34; new&#34;创建您的类的新实例是一种很好的方法。 您的问题无法通过简单而优雅的方式解决,因为您只能注入所有功能实现的界面。

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

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/

答案 3 :(得分:0)

我必须对Ninject,IoC和工厂模式应用以下方法。

步骤1: 添加了对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步创建您的课程的实例

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;