我有一个控制台应用程序,我需要根据用户的输入执行某个功能。如果用户输入"功能1" - >我执行功能1,依此类推。
我正在尝试将此项目编写为尽可能干净且通用,我想使用 IoC 和 SOLID 概念,我有点卡住。< / p>
到目前为止我有什么:
ports:
- "7777:7777"
和
public interface IFeature
{
String execFeature();
}
我的第一个想法就是在具体工厂类上有关于用户输入的切换案例,并创建具体的功能因此,但我打赌用 IoC 来做更好的方法。
我读过有关Ninject工厂扩展的内容,但我并不了解如何在我的项目中使用它。
使用IoC / Ninject进行工厂模式的最佳方法是什么?
答案 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;