依赖注入容器在构造函数中 [英] Dependency Injection container in constructor

查看:134
本文介绍了依赖注入容器在构造函数中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

解决方案


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

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



首先,你的类的用户只会知道类需要一个容器来解决它的依赖。这个数量的信息根本没有信息,因为你仍然不知道课程要依赖什么。你想为班上写单位考试吗?你必须在类里面看看它是什么类型的解决,模拟它们,并初始化每个测试的容器。这也意味着某些代码的更改将使其编译,但可能会中断一些测试:例如,当新代码依赖于尚未在容器中注册的类时,情况就是这样。



使用服务定位器时常见的次要效应是您无法确定在运行时不会在请求依赖关系时遇到异常。每个班都注册正确吗?虽然一些容器提供了检查每个接口是否被注册的可能性,但并不意味着它被注册到正确的类型。例如,可能发生一种类型是两种不同的实现注册两次,并且很难注意到任何一段代码是否可以调用该容器。



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






根据新的发展情况进行编辑:



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


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


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


为什么在构造函数中这样做是一个不好的想法和其他地方这样做
很好吗?


这样做任何地方是个坏主意你为什么要把你的集装箱注册遍布整个地方?如果您确实需要确定在运行时使用的界面的实现方式,请使用像Factory这样的内容。



所以,为什么不好?




  • 客户端类依赖于实现和接口,这不会比构造函数中的具体类更好。

  • 客户端类现在也取决于容器,并且出现服务定位器的问题(请参阅以上),现在这种方法比新建具体的类更糟糕。



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



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



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

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

public MyClass(IDependency dep)
{
}
}

在这种情况下,您不是在构造函数中使用非本地依赖关系,因为它由服务定位器解决,所以你没有依赖于实现。


Why putting a container in a constructor is so bad? For example you want to resolve a class B in the constructor of another class (C) because you need the class (B) to be used with the dependencies resolved (you start using class C the way you want it like it were B but with the dependencies resolved).

解决方案

Why putting a container in a constructor is so bad?

I suppose you mean to pass the container as a constructor argument. This is actually a variation of the Service Locator pattern, which in this context is considered to be an anti pattern. There are a couple of reasons why you may not want to do this.

First, the users of your class will only know that the class needs a container for resolving its dependencies. This amount of information is equal to no information at all, because you still don't know what the class is going to depend on. Do you want to write a unit test for the class? You have to look inside the class and see what types it is resolving, mock them and initialize the container for every test. This also means that changes on some code will let it compile but may break some tests: this is the case when the new code relies on a class which is not yet registered in the container, for instance.

A secondary effect which is common when using Service Locator is that you can never be sure that you won't be getting an exception at runtime while asking for dependencies. Is every class registered correctly? While some containers offer the possibility to check if every interface is registered, it doesn't mean it is registered to the correct type. For instance, it could happen that a type is registered twice with two different implementations and it is going to be difficult to notice if any piece of code could call the container.

A better solution to this is the Composition Root pattern. This blog post also explains why Service Locator may not be a good idea.


EDIT in light of the new developments:

Apparently you are using a third party library which relies on your classes having a default constructor. Let us assume that you have no way to influence the instantiation of your classes and that you have to let this framework do its job. Be aware that this may be a big assumption, please investigate the third party library for possibilities to do it first. At first glance, frameworks like ASP.NET WebForms and WCF don't give you many chances, but there are ways to ease the pain for these cases.

I meant just to create the container in the constructor, add the respective dependency to the container and resolve the object, which can be done by simply creating instance of the dependency object and use it to create the dependent object.

I may be missing something, but why do you need to register the dependency in the constructor? Couldn't you just resolve it in the constructor but register it somewhere else? That would still be a Service Locator, but you would at least be doing the wrong thing right.

Why doing so in the constructor is a bad idea and doing so elsewhere is fine?

Doing so anywhere but in one place is a bad idea. Why would you spread your container registration all over the place? If you really feel the need to decide what implementation of an interface to use at runtime, use something like a Factory.

So, why is it bad?

  • the client class depends on both implementation and interface, which doesn't make it better than newing the concrete class in the constructor.
  • the client class now also depends on the container, and the issues of Service Locator arise (see above), making now this approach worse than newing the concrete class.

As @Steven said, you lose all advantages of Dependency Injection. The real underlying question is: why do you absolutely want to do DI in this place? What advantages of the approach would you like to use? Based on the answer there could be several solutions. Two examples off the top of my head:

Solution 1: lose the DI for the classes being instantiated by the third party library.

Solution 2: Use a combination of Bastard Injection + Service Locator. Two wrongs could make a right in this case.

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

    public MyClass(IDependency dep)
    {
    }
}

In this case you are not using a non-local dependency in the constructor, because it is resolved by the Service Locator, so you have no dependencies on the implementation.

这篇关于依赖注入容器在构造函数中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆