为什么将容器放在构造函数中这么糟糕?
我想您的意思是将容器作为构造函数参数传递。这实际上是服务定位器模式的变体,在这种情况下被认为是反模式。您可能不想这样做有几个原因。
首先,类的用户只会知道该类需要一个容器来解决其依赖关系。这些信息量等于根本没有信息,因为你仍然不知道该类将依赖于什么。您想为班级编写单元测试吗?您必须查看类内部并查看它正在解析什么类型,模拟它们并为每个测试初始化容器。这也意味着对某些代码的更改将使其可以编译,但可能会破坏某些测试:例如,当新代码依赖于尚未在容器中注册的类时,就会出现这种情况。
使用服务定位器时常见的次要影响是,您永远无法确定在请求依赖项时不会在运行时遇到异常。每个班级都正确注册了吗?虽然某些容器提供了检查每个接口是否已注册的可能性,但这并不意味着它已注册到正确的类型。例如,可能会发生一个类型使用两个不同的实现注册两次的情况,并且很难注意到是否有任何代码片段可以调用容器。
对此的更好的解决方案是组成 根模式. 这篇博文还解释了为什么服务定位器可能不是一个好主意。
根据新的发展进行编辑:
显然您正在使用第三方库,它依赖于您的类具有默认构造函数。让我们假设您无法影响类的实例化,并且您必须让该框架完成其工作。请注意,这可能是一个很大的假设,请首先调查第三方库以了解执行此操作的可能性。乍一看,像 ASP.NET WebForms 和 WCF 这样的框架并没有给您太多机会,但有一些方法可以减轻这些情况的痛苦。
我的意思只是在构造函数中创建容器,添加
分别对容器的依赖并解析对象,其中
可以通过简单地创建依赖对象的实例来完成
用它来创建依赖对象。
我可能遗漏了一些东西,但是为什么需要在构造函数中注册依赖项?难道你不能只在构造函数中解析它,然后在其他地方注册它吗?那仍然是一个服务定位器,但你至少会做正确的错事。
为什么在构造函数中这样做是一个坏主意,而在其他地方这样做是一个坏主意
还好吗?
这样做anywhere但在一个地方是一个坏主意。为什么要将容器注册分散到各处?如果您确实觉得需要决定在运行时使用哪种接口实现,请使用类似工厂之类的东西。
那么,为什么不好呢?
- 客户端类依赖于实现和接口,这并不比在构造函数中更新具体类更好。
- 客户端类现在也依赖于容器,并且出现了服务定位器的问题(见上文),使得现在这种方法比更新具体类更糟糕。
正如@Steven 所说,您将失去依赖注入的所有优势。真正的根本问题是:为什么你绝对想在这个地方进行 DI?您想使用该方法的哪些优点?根据答案,可能有多种解决方案。我脑海中浮现出两个例子:
解决方案1:丢失第三方库实例化的类的 DI。
解决方案2:使用 Bastard Injection + Service Locator 的组合。在这种情况下,两个错误就可以构成一个正确。
public class MyClass
{
public MyClass()
: this(Container.Resolve<IDependency>())
{
}
public MyClass(IDependency dep)
{
}
}
在这种情况下,您没有在构造函数中使用非本地依赖项,因为它是由服务定位器解析的,因此您对实现没有依赖项。