2010-11-09 47 views
6

当你使用依赖注入时,你注入哪些依赖关系?我应该注入哪些依赖关系?

我以前注入所有依赖性但这样做当TDD已经发现通常有两种类型的相关性:

  • 那些其可以例如改变真正外部依赖ProductRepository
  • 纯粹为了可测试性而存在的那些例如在已提取并注入只是可测性

一种方法的类的行为的部分原因是注入所有依赖这样

public ClassWithExternalDependency(IExternalDependency external, 
    IExtractedForTestabilityDependency internal) 
{ 
    // assign dependencies ... 
} 

但我发现这可能会导致依赖性膨胀的DI注册表。

另一种方法是隐藏“可测性依赖”这样

public ClassWithExternalDependency(IExternalDependency external) 
    : this (external, new ConcreteClassOfInternalDependency()) 
{} 

internal ClassWithExternalDependency(IExternalDependency external, 
    IExtractedForTestabilityDependency internal) 
{ 
    // assign dependencies ... 
} 

这是更多的努力,但似乎使很多更有意义。缺点是不是所有对象都在DI框架中配置,因此打破了我所听到的“最佳实践”。

你会主张哪种方法,为什么?

回答

1

我相信你最好注入所有的依赖关系。如果它开始变得有点笨拙,那可能表明您需要稍微简化一些东西,或者将依赖关系移动到另一个对象中。感觉你的设计“痛苦”,你可以真正启发。

至于注册表中的依赖性膨胀,您可能会考虑使用某种传统的绑定技术,而不是手动注册每个依赖项。一些IoC容器具有内置于其中的基于约定的类型扫描绑定。例如,下面是我在使用Ninject一个卡利WPF应用程序中使用的模块的一部分:

public class AppModule : NinjectModule 
{ 
    public override void Load() 
    { 
     Bind<IShellPresenter>().To<ShellPresenter>().InSingletonScope(); 

     BindAllResults(); 
     BindAllPresenters(); 
    } 

    /// <summary> 
    /// Automatically bind all presenters that haven't already been manually bound 
    /// </summary> 
    public void BindAllPresenters() 
    { 
     Type[] types = Assembly.GetExecutingAssembly().GetTypes(); 

     IEnumerable<Type> presenterImplementors = 
      from t in types 
      where !t.IsInterface 
      && t.Name.EndsWith("Presenter") 
      select t; 

      presenterImplementors.Run(
       implementationType => 
        { 
         if (!Kernel.GetBindings(implementationType).Any()) 
          Bind(implementationType).ToSelf(); 
        }); 
    } 

即使我有几十个结果和主持人跑来跑去的,我没有额外的注册工作。

0

我当然不会注入所有的依赖关系,因为要停止?你想注入你的string依赖关系吗?我只反转了单元测试所需的依赖关系。我想存根数据库(例如见this example)。我想存根发送电子邮件。我想存根系统时钟。我想要写入文件系统。

尽可能多地反转尽可能多的依赖关系,甚至那些不需要测试的依赖关系,就是让单元测试变得越困难,越是残缺不全,就越能真正测试系统的行为。这使得你的测试更不可靠。它也使应用程序根目录中的DI配置复杂化。

+1

反转依赖关系的好处之一是能够单独测试单元。我可能会提取一个计算器类来单独测试它,但也许它不需要注入(参见第二个代码示例)。我无法在原地进行测试,因为它会使计算器课程的客户端的测试膨胀。 – Alex 2011-01-05 20:30:24

+1

注射所有注射剂,新的所有新剂型。如果没有这种区分,DI将要求所有物体在整个程序期间生活。 Newables,又名值类型,表示惰性数据和可选的一些相关的转换行为(即接受值并返回新值的方法)。注射剂,又名服务/业务类型,代表功能和可选的一些相关的程序状态。可以说,我们也有I/O类型来表示外部状态,例如文件()。 I/O类型必须是新的,但应通过抽象工厂创建,以便可以进行模拟测试。 – Jegschemesch 2014-04-17 19:12:53

0

我会手工连接所有非外部依赖关系并仅“注册”外部依赖关系。当我说非外部的时候,我的意思是属于我的组件并且为了单一责任/可测试性而被提取出来的对象,我永远不会有这样的接口的任何其他实现。外部依赖包括数据库连接,Web服务,不属于我的组件的接口。我会将它们注册为接口,因为它们的实现可以切换到stubbed集成测试。在DI容器中注册少量组件使得DI代码更易于阅读和扩展。

相关问题