gpt4 book ai didi

c# - 依赖注入(inject)不只是将问题转移到别处吗?

转载 作者:太空宇宙 更新时间:2023-11-03 12:17:47 26 4
gpt4 key购买 nike

所以通常建议使用依赖注入(inject)来帮助进行单元测试,以解决类依赖于其他类的问题。这听起来不错,但让我来谈谈我面临的问题。

这是一个没有 DI 的常规实现:

class Upper{
Middle middle = new Middle();
}

class Middle{
Lower lower = new Lower();
}

class Lower{
}

现在让我们从底部开始。 Middle 依赖于 Lower,我们真的不想这样,所以我们将为 Lower 创建一个冗余接口(interface)并将其传递给 Middle 的构造函数:

class Middle{
Lower lower;
public Middle(ILower lower){
this.lower = lower;
}
}

interface ILower{
}

class Lower : ILower{
}

这听起来不错,对吧?好吧,不是真的。我见过的大多数例子都到此为止了,忘记了某些东西需要使用 Middle 类。现在我们必须更新 Upper 才能兼容:

class Upper{
Middle middle = new Middle(new Lower());
}

这似乎不是很有用...我们所做的只是将问题提升了一个级别,并创建了一个不寻常的依赖关系:Upper 现在依赖于 Lower 了吗?这绝对不是改进。

我一定是错过了好处,但似乎 DI 只是转移了问题而不是解决了问题。事实上,这也让代码更难理解。

此外,这是一个“完整”的实现:

interface IUpper {
}

class Upper : IUpper {
Middle middle;
public Upper(IMiddle middle){
this.middle = middle;
}
}

interface IMiddle {
}

class Middle : IMiddle {
Lower lower;
public Middle(ILower lower){
this.lower = lower;
}
}

interface ILower {
}

class Lower : ILower {
}

不过,我只是在转移问题。要使用 Upper,我需要:

new Upper(new Middle(new Lower()));

现在我依赖 3 个类!

最佳答案

依赖注入(inject)只是指您创建类的方式,以便为它们提供它们的依赖项(“注入(inject)”到它们中),而不是类创建它们自己的依赖项的实例。

DI 是否只是将创建类实例的问题转移到了其他地方?

是的,这正是它的作用,这很好。

您使用的每个类实例都必须在某处实例化。问题是实例化发生在哪里,以及它是否使您的类或多或少易于管理和测试。

权衡是,如果一个类直接创建它所依赖的其他类的实例,那么当然,为该外部类调用构造函数要简单得多。你创建了那个类,它创建了一堆其他类,然后它们创建了更多类,等等。但是每个直接创建其他类的类都变得越来越难进行单元测试。当一个类的行为包括它创建的类的行为、它们创建的类的行为等等时,您不能只为一个类的行为编写测试。因此,作为对更简单的构造函数调用的返回,您得到的代码几乎无法测试,也很难维护。

依赖注入(inject)将类的依赖项的创建移出类。这使得每个单独的类 更易于测试和维护,但正如您所观察到的那样,它会产生不同的问题。现在您的构造函数调用要复杂得多,会创建各种嵌套依赖项。

解决这个问题的是依赖注入(inject)容器,也称为 IoC 容器。一些示例包括 Windsor、Autofac、Unity,还有更多。有了这些,您只需为任何类可能依赖的每个单独接口(interface)指定一个实现。

例如(这是 Windsor 语法,但它们都有点相似)

container.Register(Component.For<InterfaceA, ImplementationToUse>());
container.Register(Component.For<InterfaceB, ImplementationForThis>());
container.Register(Component.For<InterfaceC, ImplementationToUse>());

然后,如果你调用

var thingINeed = container.Resolve<InterfaceA>();

(这实际上不是我们从容器中获取类实例的方式,但那是另一回事了。)

它将弄清楚它需要创建哪些类来传递实现的构造函数。如果这些类有更多的依赖关系,它将创建这些,依此类推。

现在您已经两全其美了。您可以根据需要创建任意数量的小型可测试类,其中包含大量嵌套依赖项,所有这些都取决于抽象。如果您要尝试直接调用它们的构造函数,那将非常复杂,远远超出您问题中的示例。但你不必那样做。您可以单独考虑每个类 - 它的作用是什么,它直接依赖于哪些接口(interface)?

你仍然有一些复杂性。您现在不必调用一堆构造函数,而是必须向容器注册各个依赖项。但这是一个很好的权衡,你会领先,因为你的类是解耦和可测试的。

关于c# - 依赖注入(inject)不只是将问题转移到别处吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48981858/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com