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

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


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


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


当前回答

这更像是吹毛求疵。但是依赖注入的一个缺点是,它使开发工具更难推理和导航代码。

具体来说,如果你在代码中控制-单击/命令-单击方法调用,它将带你到接口上的方法声明,而不是具体的实现。

这实际上是松散耦合代码(由接口设计的代码)的缺点,即使不使用依赖注入(即,即使只是使用工厂)也适用。但是依赖注入的出现真正鼓励了松耦合代码的普及,所以我想我应该提到它。

而且,松散耦合代码的好处远远超过这一点,因此我称之为吹毛求疵。尽管我工作了很长时间,知道如果您试图引入依赖注入,可能会遇到这种情况。

事实上,我敢大胆地猜测,对于依赖注入的每一个“缺点”,您都会发现许多优点远远超过它。

其他回答

如果您有一个自己开发的解决方案,依赖项就会在构造函数中直接出现。或者作为方法参数,这也不难发现。尽管框架管理的依赖关系,如果走到极端,就会开始变得像魔术一样。

然而,在太多的类中有太多的依赖项是一个明显的标志,说明你的类结构搞砸了。因此,在某种程度上,依赖注入(自行开发或框架管理)可以帮助发现那些可能隐藏在暗处的突出设计问题。


为了更好地说明第二点,这里是本文的一段摘录(原始来源),我完全相信这是构建任何系统的基本问题,而不仅仅是计算机系统。

Suppose you want to design a college campus. You must delegate some of the design to the students and professors, otherwise the Physics building won't work well for the physics people. No architect knows enough about about what physics people need to do it all themselves. But you can't delegate the design of every room to its occupants, because then you'll get a giant pile of rubble. How can you distribute responsibility for design through all levels of a large hierarchy, while still maintaining consistency and harmony of overall design? This is the architectural design problem Alexander is trying to solve, but it's also a fundamental problem of computer systems development.

DI能解决这个问题吗?不。但它确实帮助你清楚地看到,如果你试图把设计每个房间的责任委托给它的居住者。

两件事:

它们需要额外的工具支持来检查配置是否有效。

例如,IntelliJ(商业版)支持检查Spring配置的有效性,并将标记出配置中的类型违反等错误。如果没有这种工具支持,就无法在运行测试之前检查配置是否有效。

这就是为什么“蛋糕”模式(Scala社区所熟知)是一个好主意的原因之一:组件之间的连接可以由类型检查器检查。而注释或XML则没有这种好处。

这使得程序的全局静态分析非常困难。

像Spring或Guice这样的框架很难静态地确定容器创建的对象图将是什么样子。尽管它们在容器启动时创建了一个对象图,但它们没有提供有用的api来描述将要创建的对象图。

这是我自己的第一反应:基本上任何模式都有同样的缺点。

学习是需要时间的 如果误解了,就会弊大于利 如果走到极端,工作量可能会超过收益

在过去的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。依赖注入是一种非常强大的模式。但它肯定会让人困惑,无论你选择什么依赖注入框架,你几乎肯定会花一些时间咒骂。

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

代码可读性。由于依赖关系隐藏在XML文件中,因此您无法轻松地找出代码流。