我试图在工作中引入依赖注入(DI)模式,我们的一位主要开发人员想知道:如果有的话,使用依赖注入模式的缺点是什么?

注意,如果可能的话,我在这里寻找一个详尽的列表,而不是关于这个主题的主观讨论。


澄清:我谈论的是依赖注入模式(参见Martin Fowler的这篇文章),而不是特定的框架,无论是基于xml的(如Spring)还是基于代码的(如Guice),还是“自滚动”的框架。


Reddit的subreddit /r/programming上正在进行一些深入的讨论/咆哮/辩论。


当前回答

以下几点:

DI增加了复杂性,通常是通过增加类的数量,因为责任分离得更多,这并不总是有益的 您的代码将(在某种程度上)耦合到您使用的依赖注入框架(或者更一般地说,如何决定实现DI模式) 执行类型解析的DI容器或方法通常会导致轻微的运行时损失(非常可以忽略不计,但它确实存在)

通常,解耦的好处是使每个任务更易于阅读和理解,但增加了编排更复杂任务的复杂性。

其他回答

控制反转(不是完全依赖注入,但已经足够接近了)最大的“缺点”是,它倾向于去掉一个点来查看一个算法的概述。这基本上就是当你有解耦的代码时所发生的事情——在一个地方查看的能力是紧密耦合的产物。

仅仅通过实现依赖注入就实现了代码的解耦,而没有实际解耦。我认为这是DI最危险的地方。

没有任何DI的代码会面临众所周知的陷入意大利面条代码的风险——一些症状是类和方法太大,做太多,不容易更改、分解、重构或测试。

大量使用DI的代码可以是Ravioli代码,其中每个小类就像一个单独的Ravioli块——它只做一件小事,并且遵循单一责任原则,这是很好的。但是从类本身来看,很难看到系统作为一个整体在做什么,因为这取决于所有这些小部分是如何组合在一起的,这是很难看到的。它看起来就像一大堆小东西。

通过避免大型类中大量耦合代码的意大利面复杂性,您将面临另一种复杂性的风险,其中有许多简单的小类,它们之间的交互非常复杂。

我不认为这是一个致命的缺点- DI仍然是非常值得的。在某种程度上,小班级只做一件事的馄饨风格可能是好的。即使过度,我也不认为它像意大利面条代码那样糟糕。但是要避免它,第一步是要意识到它可能会走得太远。点击链接了解如何避免这种情况。

在过去的6个月里,我一直在广泛使用Guice (Java DI框架)。虽然总的来说我认为它很棒(特别是从测试的角度来看),但也有一些缺点。最值得注意的是:

Code can become harder to understand. Dependency injection can be used in very... creative... ways. For example I just came across some code that used a custom annotation to inject a certain IOStreams (eg: @Server1Stream, @Server2Stream). While this does work, and I'll admit has a certain elegance, it makes understanding the Guice injections a prerequisite to understanding the code. Higher learning curve when learning project. This is related to point 1. In order to understand how a project that uses dependency injection works, you need to understand both the dependency injection pattern and the specific framework. When I started at my current job I spent quite a few confused hours groking what Guice was doing behind the scenes. Constructors become large. Although this can be largely resolved with a default constructor or a factory. Errors can be obfuscated. My most recent example of this was I had a collision on 2 flag names. Guice swallowed the error silently and one of my flags wasn't initialized. Errors are pushed to run-time. If you configure your Guice module incorrectly (circular reference, bad binding, ...) most of the errors are not uncovered during compile-time. Instead, the errors are exposed when the program is actually run.

既然我已经抱怨过了。让我说,我将继续在我当前的项目中(很可能是在下一个项目中)使用Guice。依赖注入是一种非常强大的模式。但它肯定会让人困惑,无论你选择什么依赖注入框架,你几乎肯定会花一些时间咒骂。

另外,我同意其他发帖者的观点,依赖注入可能被过度使用。

If you're using DI without an IoC container, the biggest downside is you quickly see how many dependencies your code actually has and how tightly coupled everything really is. ("But I thought it was a good design!") The natural progression is to move towards an IoC container which can take a little bit of time to learn and implement (not nearly as bad as the WPF learning curve, but it's not free either). The final downside is some developers will begin to write honest to goodness unit tests and it will take them time to figure it out. Devs who could previously crank something out in half a day will suddenly spend two days trying to figure out how to mock all of their dependencies.

类似于马克·西曼(Mark Seemann)的回答(现已删除;只有超过1万个声誉点),底线是你花时间成为一个更好的开发人员,而不是把代码拼凑在一起,然后把它扔出去/投入生产。你的企业更愿意选择哪一种?只有你能回答这个问题。