2

我已阅读了很多关于此主题的内容,但无法完全掌握它。C# - 使用依赖注入(ninject)而不是工厂模式

我正在尝试使用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的方式。

编辑:

消费类 - 在IFeatureFactoryIUILayer注入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的类:

public interface IFeature 
{ 
    IResult execFeature(); 
} 

绑定:

public override void Load() 
    { 
     Bind<IFeatureFactory>().To<BasicFeatureFactory>(); 
     Bind<IUILayer>().To<ConsoleUILayer>(); 
    } 

我怎么可以这样工厂模式转换为的IoCNinject.Extensions.Factory?请记住,创建IFeature取决于用户输入。

+0

您能否展示'IFeatureFactory'和'IFeature'的使用者如何使用这些抽象? – Steven

+0

那么,这个最好的解决方案是什么?使用Ninject.Extensions.Factory是一个好主意吗?或者像这样离开它?我该如何使用Ninject.Extensions.Factory?谢谢。 –

+0

我觉得这个问题对于你想解决什么问题有点太模糊。你是否真的要求这样来帮助你面对将来的申请?如果是这样,如果有10个人回答它,那么根据大家认为“正确”的方式来处理未来打样,你可能会得出12种不同的意见。我能说的最好的事情是,首先需要模拟您的需求**,然后担心如何使您的工具支持该需求。 –

回答

2

对我来说,看起来你有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 impelementaion:

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; 
      } 
     } 
    } 

的事情,你应该注意到我刚才解压负责“新”到另一个界面的对象。
我们不会实现两个工厂接口,因为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”结合是使用的命名结合像我一样here等同,现在是the recommended way by ninject for factories

这里需要注意的是,你自己并没有实现IFirstFeatureFactory和ISecondFeatureFactory,并且你正在使用ninject的functionallity。

这个选项的主要缺点是当我们需要添加更多的功能时,我们需要创建除了功能本身以外的另一个FeatureFactory,并且还要更改FeatureProvider来处理它。 如果功能不改变往往这个选项可以很好的和简单,但如果他们这样做它可以成为维护的噩梦,这就是为什么我建议选择2

选择二
在这个选项中,我们不会创建任何提供者类,并将所有创建逻辑放入工厂。
IFeatureFactory接口看起来与您现在使用的接口非常相似,但不是使用int作为参数,而是使用一个字符串(对于命名的绑定,我们将很快看到它会更好)。

public interface IFeatureFactory 
{ 
    IFeature CreateFeature(string featureName); 
} 

我们不会落实自己这个接口,让ninject为我们做它,但是我们需要告诉ninject使用CearteFeature的第一个参数,检测实例(FirstFeatue或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作为我们工厂的实例提供程序。

这已经解决了第一个选项中的问题,就好像我们想添加新功能一样,我们只需要创建它并将其与他的界面绑定到他的名字。
现在工厂的客户可以从工厂功能中请求这个新名称,而无需在其他类中进行更改。

Conslucsion
通过选择该选项之一以上与纯二,我们将得到让ninject内核的好处在工厂而不是做创建我们的对象都在我们自己的“新”。
尽管没有使用IoC容器没有任何问题,但如果ninject会为我们注入这些内容,那么在IFeature实现中存在大的依赖关系图时,这确实可以帮助我们。 通过执行此选项之一,我们完全使用ninject功能而不使用被称为反模式的“服务定位器”。

+0

感谢这个非常详细的答案。我最终使用了第二个选项,就像你所建议的那样,但我遇到的一个问题是 - 可以说我有基于输入“1”和“2”的FirstFeature和SecondFeature的实现。比用户输入“3” - 如何处理这个选项返回null?因为现在应用程序崩溃说“没有匹配的绑定可用,并且类型不可自行绑定”。再次感谢 –

+0

我的意思是,处理这个问题的唯一方法是在获得IFeature之前检查输入?或者ninject有办法处理这类问题。谢谢 –

+0

我几乎可以肯定,如果你重写另一个ninject deafult行为,可能会以某种方式。但在我看来,你不应该这样做,最好检查输入或在try catch块中将调用包装到工厂中。 – YuvShap

1

您可以将这些功能注入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; 
     } 
    } 
} 
+0

唯一我可能会建议的是将c'tor参数更改为“IFeature”,并使用Ninject的上下文绑定功能来确定“FirstFeature”和“SecondFeature”是什么:https://github.com/ninject/Ninject/wiki/Contextual-Binding –

+0

@JoeAmenta:我会说“最简单的事情可能可行”:)。您也可以注入内核本身并从容器中解析。 – Steven