4

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

4

1 回答 1

11

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

我想您的意思是将容器作为构造函数参数传递。这实际上是 Service Locator 模式的一种变体,在这种情况下被认为是一种反模式。您可能不想这样做有几个原因。

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

使用 Service Locator 时常见的第二个影响是,您永远无法确定在请求依赖项时不会在运行时遇到异常。每个班级都正确注册了吗?虽然某些容器提供了检查是否每个接口都已注册的可能性,但这并不意味着它已注册到正确的类型。例如,一个类型可能会使用两个不同的实现注册两次,并且很难注意到任何一段代码是否可以调用容器。

一个更好的解决方案是组合根模式这篇博文还解释了为什么服务定位器可能不是一个好主意。


根据新的发展进行编辑:

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

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

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

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

在任何地方但在一个地方这样做是一个坏主意。你为什么要把你的容器注册分散到所有地方?如果你真的觉得需要决定在运行时使用什么接口实现,请使用像工厂这样的东西。

那么,为什么不好呢?

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

正如@Steven 所说,您失去了依赖注入的所有优势。真正的潜在问题是:为什么你绝对想在这个地方做 DI?您想使用该方法的哪些优点?根据答案,可能有几种解决方案。我脑海中浮现的两个例子:

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

解决方案 2:使用混蛋注入 + 服务定位器的组合。在这种情况下,两个错误可以成为一个正确的。

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

    public MyClass(IDependency dep)
    {
    }
}

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

于 2012-07-09T07:14:49.393 回答