我试图在工作中引入依赖注入(DI)模式,我们的一位主要开发人员想知道:如果有的话,使用依赖注入模式的缺点是什么?
注意,如果可能的话,我在这里寻找一个详尽的列表,而不是关于这个主题的主观讨论。
澄清:我谈论的是依赖注入模式(参见Martin Fowler的这篇文章),而不是特定的框架,无论是基于xml的(如Spring)还是基于代码的(如Guice),还是“自滚动”的框架。
Reddit的subreddit /r/programming上正在进行一些深入的讨论/咆哮/辩论。
我试图在工作中引入依赖注入(DI)模式,我们的一位主要开发人员想知道:如果有的话,使用依赖注入模式的缺点是什么?
注意,如果可能的话,我在这里寻找一个详尽的列表,而不是关于这个主题的主观讨论。
澄清:我谈论的是依赖注入模式(参见Martin Fowler的这篇文章),而不是特定的框架,无论是基于xml的(如Spring)还是基于代码的(如Guice),还是“自滚动”的框架。
Reddit的subreddit /r/programming上正在进行一些深入的讨论/咆哮/辩论。
当前回答
两件事:
它们需要额外的工具支持来检查配置是否有效。
例如,IntelliJ(商业版)支持检查Spring配置的有效性,并将标记出配置中的类型违反等错误。如果没有这种工具支持,就无法在运行测试之前检查配置是否有效。
这就是为什么“蛋糕”模式(Scala社区所熟知)是一个好主意的原因之一:组件之间的连接可以由类型检查器检查。而注释或XML则没有这种好处。
这使得程序的全局静态分析非常困难。
像Spring或Guice这样的框架很难静态地确定容器创建的对象图将是什么样子。尽管它们在容器启动时创建了一个对象图,但它们没有提供有用的api来描述将要创建的对象图。
其他回答
以下几点:
DI增加了复杂性,通常是通过增加类的数量,因为责任分离得更多,这并不总是有益的 您的代码将(在某种程度上)耦合到您使用的依赖注入框架(或者更一般地说,如何决定实现DI模式) 执行类型解析的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。依赖注入是一种非常强大的模式。但它肯定会让人困惑,无论你选择什么依赖注入框架,你几乎肯定会花一些时间咒骂。
另外,我同意其他发帖者的观点,依赖注入可能被过度使用。
仅仅通过实现依赖注入就实现了代码的解耦,而没有实际解耦。我认为这是DI最危险的地方。