构造函数中的依赖注入容器

时间:2012-07-09 06:23:30

标签: dependency-injection

为什么将容器放在构造函数中是如此糟糕?例如,您希望在另一个类(C)的构造函数中解析类B,因为您需要将类(B)与已解析的依赖项一起使用(您可以按照您希望的方式开始使用类C,就像它是B但是解决了依赖关系)。

1 个答案:

答案 0 :(得分:9)

  

为什么将容器放在构造函数中是如此糟糕?

我想你的意思是将容器作为构造函数参数传递。这实际上是服务定位器模式的变体,在此上下文中被认为是反模式。您可能不想这样做有几个原因。

首先,您的类的用户只会知道该类需要一个容器来解析其依赖关系。这些信息量完全等于没有信息,因为您仍然不知道该课程将依赖什么。你想为全班写一个单元测试吗?您必须查看类内部并查看它正在解析的类型,模拟它们并为每个测试初始化​​容器。这也意味着某些代码的更改会让它编译但可能会破坏某些测试:例如,当新代码依赖于尚未在容器中注册的类时就是这种情况。

使用Service Locator时常见的辅助效果是,在询问依赖关系时,您永远无法确定在运行时是否会收到异常。每个班级都注册正确吗?虽然某些容器提供了检查每个接口是否已注册的可能性,但并不意味着它已注册到正确的类型。例如,可能会发生一个类型在两个不同的实现中被注册两次,并且很难注意到任何一段代码是否可以调用容器。

更好的解决方案是Composition Root patternThis blog post也解释了为什么服务定位器可能不是一个好主意。


根据新的发展编辑:

显然,您使用的第三方库依赖于具有默认构造函数的类。让我们假设你没有办法影响类的实例化,你必须让这个框架完成它的工作。请注意,这可能是一个很大的假设,请调查第三方库,以便首先进行此操作。乍一看,像ASP.NET WebForms和WCF这样的框架并没有给你很多机会,但有一些方法可以减轻这些案例的痛苦。

  

我的意思是在构造函数中创建容器,添加   对容器的相应依赖关系并解析该对象   可以通过简单地创建依赖项对象的实例来完成   用它来创建依赖对象。

我可能会遗漏一些东西,但为什么需要在构造函数中注册依赖项?难道你不能在构造函数中解决它,但在其他地方注册它?那仍然是一个服务定位器,但你至少会做错了。

  

为什么在构造函数中这样做是一个坏主意,并在其他地方这样做   很好吗?

这样做任何地方但在一个地方是个坏主意。为什么要在整个地方散布容器注册?如果您确实需要确定在运行时使用的接口的实现,请使用像Factory这样的东西。

那么,为什么不好呢?

  • 客户端类依赖于实现和接口,这并不比在构造函数中新建具体类更好。
  • 客户端类现在也依赖于容器,并且出现了Service Locator的问题(见上文),现在这种方法比新建具体类更糟糕。

正如@Steven所说,你失去了依赖注入的所有优点。真正的根本问题是:你为什么绝对想在这个地方做DI?您希望使用该方法的哪些优点?基于答案,可能有几种解决方案。我头顶的两个例子:

解决方案1 ​​:丢失第三方库实例化的类的DI。

解决方案2 :使用Bastard Injection + Service Locator的组合。在这种情况下,两个错误可能是正确的。

public class MyClass
{
    public MyClass()
        : this(Container.Resolve<IDependency>())
    {
    }

    public MyClass(IDependency dep)
    {
    }
}

在这种情况下,您没有在构造函数中使用非本地依赖项,因为它由服务定位器解析,因此您对实现没有依赖性。