C# - Ninject、IoC 和工厂模式
C# - Ninject, IoC and factory pattern
我有一个控制台应用程序,我需要在其中根据用户的输入执行特定功能。如果用户输入 "feature 1" -> 我执行功能 1,依此类推。
我正在尝试将此项目编写得尽可能干净和通用,并且我想使用 IoC 和 SOLID 概念,我有点卡住了。
我目前有:
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;
我有一个控制台应用程序,我需要在其中根据用户的输入执行特定功能。如果用户输入 "feature 1" -> 我执行功能 1,依此类推。
我正在尝试将此项目编写得尽可能干净和通用,并且我想使用 IoC 和 SOLID 概念,我有点卡住了。
我目前有:
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;