C# - 使用依赖注入 (ninject) 而不是工厂模式
C# - use dependency injection (ninject) instead of factory pattern
我已经阅读了很多关于这个主题的内容,但始终无法掌握它。
我正在尝试使用 Ninject.Extensions.Factory 而不是我的工厂来根据用户输入创建新对象。我想充分利用 Ninject 功能和 IoC 概念。
现在代码如下所示:
interface IFeatureFactory
{
IFeature createFeature(int input);
}
和:
class BasicFeatureFactory : IFeatureFactory
{
public IFeature createFeature(int input)
{
switch (input)
{
case 1:
return new FirstFeature();
case 2:
return new SecondFeature();
default:
return null;
}
}
}
将来,IFeature 会有依赖关系,所以我想用 IoC 的方式。
编辑:
consumer class - IFeatureFactory 和 IUILayer 被注入FeatureService 构造函数并使用 Ninject.
解析
private IFeatureFactory featureFactory;
private IUILayer uiHandler;
public FeatureService(IFeatureFactory featureFactory, IUILayer uiHandler)
{
this.featureFactory = featureFactory;
this.uiHandler = uiHandler;
}
public void startService()
{
int userSelection = 0;
uiHandler.displayMenu();
userSelection = uiHandler.getSelection();
while (userSelection != 5)
{
IFeature feature = featureFactory.createFeature(userSelection);
if (feature != null)
{
IResult result = feature.execFeature();
uiHandler.displayResult(result);
}
else
{
uiHandler.displayErrorMessage();
}
uiHandler.displayMenu();
userSelection = uiHandler.getSelection();
}
}
和 IFeature class:
public interface IFeature
{
IResult execFeature();
}
绑定:
public override void Load()
{
Bind<IFeatureFactory>().To<BasicFeatureFactory>();
Bind<IUILayer>().To<ConsoleUILayer>();
}
如何使用 Ninject.Extensions.Factory[=53= 将此 Factory Pattern 转换为 IoC ]?请记住 IFeature 的创建取决于用户输入。
您可以将特征注入 BasicFeatureFactory
的构造函数。
class BasicFeatureFactory : IFeatureFactory
{
FirstFeature feature1;
SecondFeature feature2;
public BasicFeatureFactory(FirstFeature feature1, SecondFeature feature2) {
this.feature1 = feature1;
this.feature2 = feature2;
}
public IFeature createFeature(int input) {
switch (input) {
case 1: return this.feature1;
case 2: return this.feature2;
default: return null;
}
}
}
对我来说,看起来您有 2 个选项来重构您的代码以获得 ninject 的全部好处。
您现在的工作方式与 pure di 没有什么不同(这没有任何问题,并且在某些情况下更好)但是正如您所说,您想充分利用 ninject 功能。
选项一
不是将 IFeatureFactory 注入 FeatureService,而是注入 IFeatureProvider 接口,它看起来像这样:
public interface IFeatureProvider
{
IFeature GetFeature(int featureType);
}
现在您的 FeatureService 将从该提供商而不是工厂获取请求的功能。
您将需要实现 IFeatureProvider,为此您还需要 2 个接口 IFirstFeatureFactory 和 ISecondFeatureFactory:
public interface IFirstFeatureFactory
{
IFeature CreateFirstFeature();
}
public interface ISecondFeatureFactory
{
IFeature CreateSecondFeature();
}
现在 IFeatureProvider 实现:
public class FeatureProvider: IFeatureProvider
{
private readonly IFirstFeatureFactory _firstFeatureFactory;
private readonly ISecondFeatureFactory _secondFeatureFactory;
public FeatureProvider(IFirstFeatureFactory firstFeatureFactory, ISecondFeatureFactory secondFeatureFactory)
{
_firstFeatureFactory=firstFeatureFactory;
_secondFeatureFactory=secondFeatureFactory;
}
public IFeautre GetFeature(int featureType)
{
switch(featureType)
{
case 1:
return _firstFeatureFactory.CreateFirstFeature();
case 2:
return _secondFeatureFactory.CreateSecondFeature();
default:
return null;
}
}
}
你应该注意到我只是将负责 'new' 的对象提取到另一个界面中。
我们不会实现这两个工厂接口,因为如果我们将其正确绑定,ninject 会为我们完成。
绑定:
Bind<IFeature>().ToFeature<FirstFeature>().NamedLikeFactoryMethod((IFirstFeatureFactory o) => o.CreateFirstFeature());
Bind<IFeature>().ToFeature<SecondFeature>().NamedLikeFactoryMethod((ISecondFeatureFactory o) => o.CreateSecondFeature());
Bind<IFirstFeatureFactory>().ToFactory();
Bind<ISecondFeatureFactory>().ToFactory();
Bind<IFeatureProvider>().To<FeatureProivder>();
这个 'NameLikeFactoryMethod' 绑定等同于我使用的命名绑定 and it is now the recommended way by ninject for factories。
这里需要注意的重要一点是,您没有自己实现 IFirstFeatureFactory 和 ISecondFeatureFactory,而是为此使用了 ninject 功能。
此选项的主要缺点是,当我们需要添加更多功能时,除了功能本身之外,我们还需要创建另一个 FeatureFactory 并更改 FeatureProvider 来处理它。
如果功能不经常更改,此选项可能很好且简单,但如果更改,则可能成为维护噩梦,这就是我建议选项 2 的原因。
选项二
在此选项中,我们根本不会创建任何提供程序 class,并将所有创建逻辑放在工厂中。
IFeatureFactory 界面看起来与您现在拥有的界面非常相似,但我们将使用字符串而不是使用 int 作为参数(我们很快就会看到,它更适合命名绑定)。
public interface IFeatureFactory
{
IFeature CreateFeature(string featureName);
}
我们不会自己实现这个接口,而是让 ninject 为我们实现,但是我们需要告诉 ninject 使用 CearteFeature 的第一个参数来检测要实例化哪个实现( FirstFeature 或 SecondFeature)。
为此,我们将需要具有此行为的自定义实例提供程序,因为 StandardInstanceProvider 使用其他约定来选择要实例化的实现(默认约定 in this article)。
幸运的是 ninject 展示了我们如何通过 UseFirstArgumentAsNameInstanceProvider 快速实现它。
现在绑定:
Bind<IFeature>().To<FirstFeature>().Named("FirstFeature");
Bind<IFeature>().To<FirstFeature>().Named("SecondFeature");
Bind<IFeatureFactory>().ToFactory(() => new UseFirstArgumentAsNameInstanceProvider());
注意事项:
- 我们不会自己实现工厂接口
- 我们为每个实现使用命名绑定,我们将根据我们将传递给工厂方法的 featureName 从工厂获取实现(这就是为什么我更喜欢参数为字符串)。
请注意,如果您传递的特征名称不是 "FirstFeature" 或 "SecondFeature",将抛出异常。
- 如前所述,我们使用 UseFirstArgumentAsNameInstanceProvider 作为工厂的实例提供程序。
这已经解决了第一个选项中的问题,就好像我们想要添加新功能一样,我们只需要创建它并使用他的名字将其绑定到他的界面。
现在工厂的客户可以使用这个新名称向工厂功能询问,而无需更改其他 classes.
结论
通过选择上面的选项之一而不是 pure di,我们将获得让 ninject 内核在工厂中创建我们的对象而不是我们自己完成所有 'new' 的好处。
尽管不使用 IoC 容器并没有错,但在 IFeature 实现中存在大量依赖关系图的情况下,这确实可以帮助我们,因为 ninject 会为我们注入它们。
通过执行其中一个选项,我们可以完全使用 ninject 功能,而无需使用被视为反模式的 'Service locator'。
我已经阅读了很多关于这个主题的内容,但始终无法掌握它。
我正在尝试使用 Ninject.Extensions.Factory 而不是我的工厂来根据用户输入创建新对象。我想充分利用 Ninject 功能和 IoC 概念。
现在代码如下所示:
interface IFeatureFactory
{
IFeature createFeature(int input);
}
和:
class BasicFeatureFactory : IFeatureFactory
{
public IFeature createFeature(int input)
{
switch (input)
{
case 1:
return new FirstFeature();
case 2:
return new SecondFeature();
default:
return null;
}
}
}
将来,IFeature 会有依赖关系,所以我想用 IoC 的方式。
编辑:
consumer class - IFeatureFactory 和 IUILayer 被注入FeatureService 构造函数并使用 Ninject.
解析 private IFeatureFactory featureFactory;
private IUILayer uiHandler;
public FeatureService(IFeatureFactory featureFactory, IUILayer uiHandler)
{
this.featureFactory = featureFactory;
this.uiHandler = uiHandler;
}
public void startService()
{
int userSelection = 0;
uiHandler.displayMenu();
userSelection = uiHandler.getSelection();
while (userSelection != 5)
{
IFeature feature = featureFactory.createFeature(userSelection);
if (feature != null)
{
IResult result = feature.execFeature();
uiHandler.displayResult(result);
}
else
{
uiHandler.displayErrorMessage();
}
uiHandler.displayMenu();
userSelection = uiHandler.getSelection();
}
}
和 IFeature class:
public interface IFeature
{
IResult execFeature();
}
绑定:
public override void Load()
{
Bind<IFeatureFactory>().To<BasicFeatureFactory>();
Bind<IUILayer>().To<ConsoleUILayer>();
}
如何使用 Ninject.Extensions.Factory[=53= 将此 Factory Pattern 转换为 IoC ]?请记住 IFeature 的创建取决于用户输入。
您可以将特征注入 BasicFeatureFactory
的构造函数。
class BasicFeatureFactory : IFeatureFactory
{
FirstFeature feature1;
SecondFeature feature2;
public BasicFeatureFactory(FirstFeature feature1, SecondFeature feature2) {
this.feature1 = feature1;
this.feature2 = feature2;
}
public IFeature createFeature(int input) {
switch (input) {
case 1: return this.feature1;
case 2: return this.feature2;
default: return null;
}
}
}
对我来说,看起来您有 2 个选项来重构您的代码以获得 ninject 的全部好处。
您现在的工作方式与 pure di 没有什么不同(这没有任何问题,并且在某些情况下更好)但是正如您所说,您想充分利用 ninject 功能。
选项一
不是将 IFeatureFactory 注入 FeatureService,而是注入 IFeatureProvider 接口,它看起来像这样:
public interface IFeatureProvider
{
IFeature GetFeature(int featureType);
}
现在您的 FeatureService 将从该提供商而不是工厂获取请求的功能。
您将需要实现 IFeatureProvider,为此您还需要 2 个接口 IFirstFeatureFactory 和 ISecondFeatureFactory:
public interface IFirstFeatureFactory
{
IFeature CreateFirstFeature();
}
public interface ISecondFeatureFactory
{
IFeature CreateSecondFeature();
}
现在 IFeatureProvider 实现:
public class FeatureProvider: IFeatureProvider
{
private readonly IFirstFeatureFactory _firstFeatureFactory;
private readonly ISecondFeatureFactory _secondFeatureFactory;
public FeatureProvider(IFirstFeatureFactory firstFeatureFactory, ISecondFeatureFactory secondFeatureFactory)
{
_firstFeatureFactory=firstFeatureFactory;
_secondFeatureFactory=secondFeatureFactory;
}
public IFeautre GetFeature(int featureType)
{
switch(featureType)
{
case 1:
return _firstFeatureFactory.CreateFirstFeature();
case 2:
return _secondFeatureFactory.CreateSecondFeature();
default:
return null;
}
}
}
你应该注意到我只是将负责 'new' 的对象提取到另一个界面中。
我们不会实现这两个工厂接口,因为如果我们将其正确绑定,ninject 会为我们完成。
绑定:
Bind<IFeature>().ToFeature<FirstFeature>().NamedLikeFactoryMethod((IFirstFeatureFactory o) => o.CreateFirstFeature());
Bind<IFeature>().ToFeature<SecondFeature>().NamedLikeFactoryMethod((ISecondFeatureFactory o) => o.CreateSecondFeature());
Bind<IFirstFeatureFactory>().ToFactory();
Bind<ISecondFeatureFactory>().ToFactory();
Bind<IFeatureProvider>().To<FeatureProivder>();
这个 'NameLikeFactoryMethod' 绑定等同于我使用的命名绑定
这里需要注意的重要一点是,您没有自己实现 IFirstFeatureFactory 和 ISecondFeatureFactory,而是为此使用了 ninject 功能。
此选项的主要缺点是,当我们需要添加更多功能时,除了功能本身之外,我们还需要创建另一个 FeatureFactory 并更改 FeatureProvider 来处理它。
如果功能不经常更改,此选项可能很好且简单,但如果更改,则可能成为维护噩梦,这就是我建议选项 2 的原因。
选项二
在此选项中,我们根本不会创建任何提供程序 class,并将所有创建逻辑放在工厂中。
IFeatureFactory 界面看起来与您现在拥有的界面非常相似,但我们将使用字符串而不是使用 int 作为参数(我们很快就会看到,它更适合命名绑定)。
public interface IFeatureFactory
{
IFeature CreateFeature(string featureName);
}
我们不会自己实现这个接口,而是让 ninject 为我们实现,但是我们需要告诉 ninject 使用 CearteFeature 的第一个参数来检测要实例化哪个实现( FirstFeature 或 SecondFeature)。
为此,我们将需要具有此行为的自定义实例提供程序,因为 StandardInstanceProvider 使用其他约定来选择要实例化的实现(默认约定 in this article)。
幸运的是 ninject 展示了我们如何通过 UseFirstArgumentAsNameInstanceProvider 快速实现它。
现在绑定:
Bind<IFeature>().To<FirstFeature>().Named("FirstFeature");
Bind<IFeature>().To<FirstFeature>().Named("SecondFeature");
Bind<IFeatureFactory>().ToFactory(() => new UseFirstArgumentAsNameInstanceProvider());
注意事项:
- 我们不会自己实现工厂接口
- 我们为每个实现使用命名绑定,我们将根据我们将传递给工厂方法的 featureName 从工厂获取实现(这就是为什么我更喜欢参数为字符串)。
请注意,如果您传递的特征名称不是 "FirstFeature" 或 "SecondFeature",将抛出异常。 - 如前所述,我们使用 UseFirstArgumentAsNameInstanceProvider 作为工厂的实例提供程序。
这已经解决了第一个选项中的问题,就好像我们想要添加新功能一样,我们只需要创建它并使用他的名字将其绑定到他的界面。
现在工厂的客户可以使用这个新名称向工厂功能询问,而无需更改其他 classes.
结论
通过选择上面的选项之一而不是 pure di,我们将获得让 ninject 内核在工厂中创建我们的对象而不是我们自己完成所有 'new' 的好处。
尽管不使用 IoC 容器并没有错,但在 IFeature 实现中存在大量依赖关系图的情况下,这确实可以帮助我们,因为 ninject 会为我们注入它们。
通过执行其中一个选项,我们可以完全使用 ninject 功能,而无需使用被视为反模式的 'Service locator'。