我正在尝试在这里将DI作为一种模式进行介绍,我们的一位主要开发人员想知道:使用依赖注入模式的缺点-如果有的话-有什么缺点?

请注意,我正在这里寻找(如果可能)详尽列表,而不是对该主题进行主观讨论。


澄清:我在说的是依赖注入模式(请参阅Martin Fowler的本文),而不是特定的框架,无论是基于XML(例如Spring)还是基于代码(例如Guice)还是“自卷式”。


编辑:在这里/ r / programming进行了一些很棒的进一步讨论/讨论/辩论。

评论

指定我们是要讨论DI本身还是支持DI的特定类型的工具(是否基于XML)可能是个好主意。

区别在于,我不是在寻找仅适用于特定框架(例如“ XML糟透了”)的答案。 :)我正在寻找适用于Fowler概述的DI概念的答案:martinfowler.com/articles/injection.html

我认为一般而言,DI(构造函数,setter或方法)完全没有缺点。无需任何开销即可解耦和简化。

@kissaki与二传手相距甚远。最好制造可用于建筑的物体。如果您不相信我,只需尝试通过“ setter”注入向对象添加另一个协作者,您肯定会获得NPE...。

捍卫服务定位器

#1 楼

有两点:


DI增加了复杂性,通常是通过增加类的数量来增加责任的分离,这并不总是有益的。
您的代码将是(某种程度上)耦合到您使用的依赖项注入框架(或更笼统地说,您决定执行DI模式的方式)
执行类型解析的DI容器或方法通常会造成轻微的运行时代价(非常微不足道,但确实存在)

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

评论


-类的分离降低了复杂性。许多类不会使应用程序变得复杂。 -您应该只对应用程序根目录的DI框架具有依赖性。

–罗伯特
2010-3-9在9:04

我们是人类我们的短期记忆是有限的,不能同时处理许多。对于类,对于方法,函数,文件或用于开发程序的任何构造,都是相同的。

–Håvard S
2010-3-9在9:20

@Havard S:是的,但是5个真正复杂的类并不比15个简单的类简单。降低复杂度的方法是减少程序员编写代码所需的工作集-复杂的,高度相互依赖的类无法实现这一目标。

–kyoryu
10 Mar 9 '10 at 10:01

@kyoryu我想我们都同意这一点。请记住,复杂性与耦合不同。减少耦合会增加复杂性。我并不是说DI是一件坏事,因为它会增加类的数量,而是强调与之相关的潜在缺点。 :)

–Håvard S
2010年3月9日10:31



+1为“ DI增加了复杂性”在DI及其它之外的世界都是如此。 (几乎)每当我们增加灵活性时,我们都会增加复杂性。一切都与平衡有关,首先要了解其优点和缺点。当人们说“没有缺点”时,这肯定表明他们还没有完全理解这一点。

–唐布兰森
2010年3月9日18:37

#2 楼

与面向对象的编程,样式规则以及几乎所有其他内容经常遇到的相同基本问题。实际上,很可能会做太多的抽象工作,增加过多的间接访问,并且可能过度地在错误的地方过度应用良好的技术。

您应用的每种模式或其他构造带来了复杂性。抽象和间接分散信息,有时将无关紧要的细节移开,但有时也使得更难于准确了解正在发生的事情。您所应用的每条规则都带来了灵活性,排除了可能只是最佳方法的选择。

关键是编写能起作用,健壮,易读和可维护的代码。您是软件开发人员,而不是象牙塔的构建者。

相关链接

http://thedailywtf.com/Articles/The_Inner-Platform_Effect.aspx
< http://www.joelonsoftware.com/articles/fog0000000018.html



可能是最简单的依赖项注入形式(不要笑)是一个参数。从属代码依赖于数据,并且通过传递参数的方式来注入数据。

是的,它很愚蠢,并且没有解决依赖项注入的面向对象问题,但是函数式程序员会告诉您(如果您具有一流的函数),这是您唯一需要的依赖项注入。这里的重点是举一个简单的例子,并说明潜在的问题。

让我们使用这个简单的传统函数-C ++语法在这里并不重要,但我必须以某种方式将其拼写...

void Say_Hello_World ()
{
  std::cout << "Hello World" << std::endl;
}


我有一个要提取并注入的依赖项-文本“ Hello World”。足够容易...

void Say_Something (const char *p_text)
{
  std::cout << p_text << std::endl;
}


那比原来的那么僵硬?好吧,如果我决定输出应为Unicode怎么办。我可能想从std :: cout切换到std :: wcout。但这意味着我的字符串必须是wchar_t,而不是char。要么必须更改每个调用者,要么(更合理地说)是,旧的实现被替换为适配器,该适配器转换字符串并调用新的实现。

在那里的维护工作就不会了。

如果看起来很琐碎,请从Win32 API看一下这个真实函数...

http:/ /msdn.microsoft.com/zh-CN/library/ms632680%28v=vs.85%29.aspx

要处理的12个“依赖项”。例如,如果屏幕分辨率变得非常高,也许我们将需要64位坐标值-以及另一个版本的CreateWindowEx。是的,已经有一个较旧的版本存在,大概是在幕后映射到了较新的版本...

http://msdn.microsoft.com/zh-cn/library/ ms632679%28v = vs.85%29.aspx

那些“依赖项”不只是原始开发人员的问题-使用该接口的每个人都必须查找依赖项是什么,它们如何指定它们的含义以及它们的含义,并弄清楚该应用程序应做什么。在这里,“合理的默认值”可以使生活变得更加简单。

面向对象的依赖注入在原理上没有什么不同。无论是在源代码文本中还是在开发人员时,编写类都是一项开销,并且如果根据某些相关对象规范编写该类以提供相关性,则即使有需要,该相关对象也被锁定为支持该接口。替换该对象的实现。

所有这些都不应该被视为声称依赖注入是不好的-远非如此。但是,任何好的技术都可能在错误的地方过度使用。正如不是每个字符串都需要提取出来并转化为参数一样,并不是每个低级行为都需要从高级对象中提取出来并转化为可注入的依赖关系。

评论


依赖注入没有任何这些缺点。它仅更改传递对对象的依赖关系的方式,不会增加复杂性或灵活性。恰恰相反。

–木崎
2011-2-10在10:13

@Kissaki-是的,它改变了您传递依赖关系的方式。而且,您必须编写代码来处理依赖关系的传递。并且在执行此操作之前,您必须确定要传递的依赖项,并以某种方式对它们进行定义,这对没有编写依赖项代码的人是有意义的,等等。您可以避免运行时影响(例如,将策略参数用于模板)在C ++中),但这仍然是您必须编写和维护的代码。如果它是有道理的,那么这是一个很小的价格,却能获得丰厚的回报,但是如果您假装没有价格可付,那就意味着您将在没有收益的情况下付出该价格。

–Steve314
2011-2-10 17:30

@Kissaki-关于灵活性,一旦您指定了依赖项,就被锁定了-其他人已经写了依赖项以根据该规范进行注入。如果您需要一个新的实现来实现需要稍微不同的依赖关系的依赖代码-很难,那么您就被锁定了。该开始为这些依赖关系编写一些适配器了(这会增加一些开销和另一层抽象)。

–Steve314
2011-02-10 17:59

如果您考虑重载,则不确定我是否理解此示例。要输出文字“ Hello World”,我们使用()签名。要输出8位字符串,请使用(char)签名。要输出unicode,请输入重载(wchar_t)。显然,在这种情况下,(wchar_t)是其他人在后台调用的那个。那里不需要重写太多代码。我想念什么吗?

–ingredient_15939
2014年3月19日在17:15

@ ingredient_15939-是的,这是一个简化的玩具示例。如果您确实在执行此操作,则只使用重载而不是适配器,因为复制代码的成本小于适配器的成本。但是请注意,复制代码通常是一件坏事,而使用适配器来避免复制代码是另一种不错的技术(有时可能会在错误的地方使用过多的代码)。

–Steve314
2014年3月24日在2:45

#3 楼

这是我自己的初步反应:任何模式基本上都有相同的缺点。


学习需要时间,如果误解会导致弊大于利
采取极端措施可能比证明收益合理得多。


评论


为什么要花时间学习?与ejb相比,我认为这使事情变得简单。

– fastcodejava
2010年3月9日在8:31

考虑到您不想进行主观讨论,这似乎有些花招。我建议(如有必要,共同建立)一个实际的检查示例。建立没有DI的样本将是一个很好的起点。然后我们可以挑战它要求更改并检查影响。 (顺便说一下,这对我建议睡觉非常方便。)

–杰西·米利坎(Jesse Millikan)
10 Mar 9 '10 at 8:41



这确实需要一些示例来支持,并且已经教了数十个人DI,我可以告诉您,这确实是一个非常简单的概念,需要学习并开始付诸实践。

–寒颤
10 Mar 9 '10 at 9:18

我真的不认为DI是一种模式。它(与IoC结合使用)实际上更像是一种编程模型-如果您完全遵循它们,则您的代码看起来比“典型”的伪过程OO更加类似于Actor。

–kyoryu
2010年3月9日上午10:04

+1我正在使用Symfony 2,DI遍及整个用户代码,只是在使用它而烦恼。

–MGP
13年2月22日在15:23

#4 楼

控制反转的最大“缺点”(不是完全的DI,但足够接近)是,它倾向于消除只看算法概述的问题。不过,这基本上就是在将代码解耦后会发生的事情-能够在一个地方查看是紧密耦合的产物。

评论


但是可以肯定的是,“缺点”是由于我们要解决的问题的性质而造成的,因此将其去耦,以便我们可以轻松地更改实现,这意味着没有地方可以去看,并且可以去看它有什么相关性?我能想到的唯一情况是在调试中,调试环境应该能够逐步实现。

–vickirk
2010-3-11在12:55

这就是为什么单词“ downside”用引号引起来的原因:)松散耦合和强大的封装排除了“一个地方可以看到所有事物”的定义。如果您觉得我反对DI / IoC,请参阅我对哈佛大学S回应的评论。

–kyoryu
2010-3-12的2:37

我同意(我什至投票赞成你)。我只是在说清楚。

–vickirk
2010-3-12在8:20

@vickirk:我想不利的一面是人类很难理解。从人类难以理解的角度上讲,去耦事物会增加复杂性,并且需要更长的时间才能完全理解。

–伯克·弗洛伊德·汉森(Bjarke Freund-Hansen)
2010-12-30 9:42

相反,DI的一个优点是您可以查看单个类的构造函数,并立即计算出它所依赖的类(即哪些类需要完成其工作)。对于其他代码而言,这要困难得多,因为该代码可以随意创建类。

– Contango
2013年9月26日13:38



#5 楼

在过去的6个月中,我一直在广泛使用Guice(Java DI框架)。总体而言,我认为它很棒(尤其是从测试的角度来看),但存在某些缺点。最值得注意的是:



代码可能变得更难以理解。依赖注入可以以非常有创意的方式使用。例如,我刚遇到了一些代码,这些代码使用自定义批注注入特定的IOStreams(例如:@ Server1Stream,@ Server2Stream)。尽管这样做确实有效,但我承认它具有一定的优雅性,但它使理解Guice注入成为理解代码的先决条件。

学习项目时的学习曲线更高。这与第1点有关。为了了解使用依赖注入的项目的工作方式,您需要了解依赖注入模式和特定框架。当我开始目前的工​​作时,我花了很多时间来迷惑Guice在幕后所做的事情。

构造函数变得很大。尽管可以使用默认构造函数或工厂在很大程度上解决此问题。

错误可以被混淆。我最近的例子是我在两个标志名称上发生了冲突。 Guice默默地吞下了错误,并且我的标志之一未初始化。

错误被推送到运行时。如果您错误地配置了Guice模块(循环引用,绑定错误,...),则在编译期间不会发现大多数错误。相反,当程序实际运行时会暴露错误。

现在我抱怨了。让我说,我将继续(愿意)在我当前的项目中,很可能在下一个项目中使用Guice。依赖注入是一种非常强大的模式。但这肯定会造成混淆,您几乎肯定会花一些时间在您选择的任何依赖注入框架上进行诅咒。

此外,我也同意其他海报认为依赖注入会被过度使用。

评论


我不明白为什么人们不会为我做更多的“将错误推送到运行时”的事情,这会破坏交易,静态类型化和编译时错误是给开发人员的最大礼物,我不会抛出他们为了任何东西而离开

–Richard Tingle
15年7月23日在14:06

@RichardTingle,将在应用程序启动DI模块时首先初始化IMO,因此,模块将在应用程序启动后立即显示模块中的任何错误配置,而不是几天或时间之后。也可以增量加载模块,但是如果我们在初始化应用程序逻辑之前通过限制模块加载来遵循DI的精神,则可以成功地将不良绑定隔离到应用程序的启动。但是,如果我们将其配置为服务定位器反模式,那么这些错误的绑定肯定会令人惊讶。

– k4vin
16/12/22在13:24

@RichardTingle我知道它永远不会与编译器提供的安全网相似,而是将DI作为如框所示正确使用的工具,然后将这些运行时错误限制为应用程序初始化。然后,我们可以将应用程序初始化视为DI模块的一种编译阶段。根据我的经验,大多数情况下,如果应用程序启动,则其中不会存在错误的绑定或错误的引用。 PS-我一直在C#中使用NInject

– k4vin
16/12/22在13:29

@RichardTingle-我同意你的观点,但这是为了获得松散耦合的,可测试的代码而进行的权衡。正如k4vin所说,在初始化时会发现缺少依赖项,并且使用接口仍将有助于解决编译时错误。

– Andrei Epure
17年2月28日在11:52

我会在您的列表中添加“很难保持代码干净”。在完全测试没有注册的代码之前,您永远不会知道是否可以删除注册。

–fernacolo
'18 Sep 21'在0:26

#6 楼

我认为这样的列表不存在,但是请尝试阅读这些文章:


DI可以掩盖代码(如果您使用的不是良好的IDE)
根据Bob叔叔的说法,滥用IoC可能会导致错误的代码。
需要注意过度设计和产生不必要的多功能性。


评论


我很好奇,当有人要钱的时候,为什么会本着“没有任何东西”的精神来回答,而那些包含一些与问题有关的信息却没有呢?

–加布里埃尔Ščerbák
2010年3月9日在9:21

-1是因为我不是在寻找链接集合,而是在寻找实际的答案:如何总结每篇文章的观点?然后我改为投票。请注意,尽管看起来前两篇实际上是在揭穿DI的底片,但文章本身还是很有趣的?

– Epaga
10 Mar 9 '10 at 9:51



我不知道最喜欢的答案是否也被否决了,因为它真的根本无法回答问题。)我知道您的要求和回答,我不是在要求赞成,我只是糊涂了为什么我的答案无济于事,而显然说DI的缺点是它太酷了,这很有帮助。

–加布里埃尔Ščerbák
2010年3月9日10:06

#7 楼

没有任何DI的代码就存在陷入Spaghetti代码的众所周知的风险-有些症状是类和方法太大,做得太多并且不容易更改,分解,重构或测试。 >
大量使用DI的代码可以是Ravioli代码,其中每个小类都像一个单独的馄饨块-它做一件小事情,并且遵循单一责任原则,这很好。但是单独查看类很难看到系统整体的功能,因为这取决于所有这些小部分如何组合在一起,这很难看到。它看起来像一大堆小东西。

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

我认为这不是致命的缺点-DI仍然非常值得。某种程度上只做一件事的小饺子馄饨风格可能是好的。即使过多,我也认为这不是意大利面条代码的坏处。但是要意识到它可能采取的措施过于严格,这是避免它的第一步。请按照链接讨论如何避免这种情况。

评论


是的,我喜欢这个术语“馄饨代码”;当我谈论DI的缺点时,我会更冗长地表达它。尽管如此,我无法想象没有DI就能用Java开发任何真正的框架或应用程序。

–霍华德·刘易斯·希普
2011年2月11日在18:57

#8 楼

如果您有自己开发的解决方案,则依赖关系就在构造函数中浮出水面。或作为方法参数,也不太难发现。尽管将框架管理的依赖项放到了极致,但是它开始看起来就像魔术一样。
但是,在太多的类中拥有太多的依赖项则清楚地表明您是在弄糟类结构。因此,以某种方式进行依赖项注入(本地生成或由框架管理)可以帮助解决明显的设计问题,而这些问题本可以隐藏在黑暗中。

为更好地说明第二点,以下摘录自我全心全意地相信这篇文章(原始资料)是构建任何系统而不仅仅是计算机系统的根本问题。

假设您要设计一个大学校园。您必须将一些设计委托给学生和教授,否则物理大楼对物理人员而言将无法正常工作。没有任何一位建筑师对物理学家需要自己做的事情了解得足够多。但是您不能将每个房间的设计委托给它的占用者,因为那样您会得到一堆巨大的瓦砾。
如何在较大的层次结构的各个层次上分配设计责任,同时又保持一致性和整体设计的和谐?这是Alexander试图解决的体系结构设计问题,但这也是计算机系统开发的基本问题。

DI可以解决这个问题吗?不会。但是,它确实可以帮助您清楚地知道是否要将设计每个房间的责任委托给其居住者。

#9 楼

您只是通过实现依赖注入而没有实际将其解耦来使代码解耦的错觉。我认为这是DI最危险的事情。

#10 楼

让我对DI感到困惑的一件事是,假设所有注入的对象都易于实例化并且不产生副作用-OR-依赖关系使用得如此频繁,以至于它超过了任何关联的实例化成本。

当消费类中不经常使用依赖项时,这可能很重要。例如IExceptionLogHandlerService之类的东西。显然,此类服务很少在类中被调用(希望:))-大概仅在需要记录的异常时才调用。但是规范的构造函数注入模式...

Public Class MyClass
    Private ReadOnly mExLogHandlerService As IExceptionLogHandlerService

    Public Sub New(exLogHandlerService As IExceptionLogHandlerService)
        Me.mExLogHandlerService = exLogHandlerService
    End Sub

     ...
End Class


...要求提供此服务的“实时”实例,这会破坏成本/副作用需要到那里。不太可能,但是如果构造此依赖项实例涉及服务/数据库命中,配置文件查找或锁定资源直到处置该怎么办?如果此服务是按需构建,位于服务所在地或由工厂生成的(都有自己的问题),那么您将仅在必要时承担构建成本。

现在,这是一种普遍接受的软件设计原则,即构造一个对象很便宜并且不会产生副作用。尽管这是一个很好的概念,但并非总是如此。但是,使用典型的构造函数注入基本上要求是这种情况。意味着在创建依赖项的实现时,必须在设计时考虑到DI。也许您本可以使对象构造的成本更高,以在其他地方获得收益,但是如果要注入此实现,则可能会迫使您重新考虑该设计。

顺便说一句,某些技术可以通过允许延迟注入已注入的依赖项来减轻此确切问题,例如,提供一个Lazy<IService>实例作为依赖项。这将改变您的从属对象的构造函数,从而使您更加意识到实现细节,例如对象构造费用,这可能也是不希望的。

评论


我理解您在说什么,但我不认为“当创建依赖的实现时,必须在设计时就考虑到DI”是不公平的-我认为更准确的说法是“ DI有效与在实例化成本方面最佳实践以及在构造过程中没有副作用或失败模式的类一起使用时,“最佳”。最坏的情况是,您总是可以注入一个延迟代理实现,以延迟对实际对象的分配,直到首次使用为止。

–乔利·罗杰(Jolly Roger)
2011年11月7日在20:28

任何现代的IoC容器都将允许您为特定的抽象类型/接口(总是唯一,单例,http作用域等)指定对象的生存期。您还可以提供工厂方法/委托以使用Func 或Lazy 延迟实例化它。

–德米特里S.
2011年12月14日下午6:47

发现。实例化依赖关系不必要地消耗了内存。我通常使用带有“ getter”的“延迟加载”功能,这些getter仅在被调用时实例化一个类。您可以提供不带参数的默认构造函数,也可以提供接受实例化依赖性的后续构造函数。在第一种情况下,仅当在try / catch语句中发生错误时,才使用this.errorLogger.WriteError(ex)实例化实现IErrorLogger的类。

– John Bonfardeci
18-2-23在18:07



#11 楼

这更像是挑剔。但是依赖项注入的缺点之一是,它使开发工具难以推理和导航代码。

具体来说,如果您按住Control-Click / Command-Click的方法调用代码,它会将您带到接口上的方法声明而不是具体的实现。

这实际上是松散耦合代码(由接口设计的代码)的缺点,甚至适用于如果您不使用依赖注入(即,即使您只是使用工厂)。但是依赖注入的出现真正促使了松散耦合代码的普及,所以我想提一提。

此外,松散耦合代码的好处远不止于此,因此我称之为顽皮。尽管我花了很长时间才知道,如果尝试引入依赖项注入,这可能会带来一些挫折。

实际上,我敢于猜测每个“缺点”,您可以找到依赖注入,远远超过了它的许多缺点。

评论


Ctrl-shift-B与Resharper一起带您实现

– adrianm
2010年3月9日10:50

但是话又说回来,我们(在理想的世界中)不会至少有两种实现,即至少有一种实际实现和用于单元测试的模拟实现;-)

–vickirk
2010-3-11在12:48

在Eclipse中,如果按住Ctrl键并将鼠标悬停在方法调用代码上,它将显示一个菜单以打开“声明”或“实现”。

–山姆·哈斯勒
2011-02-10 9:32

如果您使用的是接口定义,请突出显示方法名称并按Ctrl + T以显示该方法的类型层次结构-您将在类型层次结构树中看到该方法的每个实现者。

–资格
2011年2月11日在20:12

#12 楼

基于构造函数的依赖注入(无需借助神奇的“框架”)是一种构造OO代码的干净而有益的方法。在我见过的最好的代码库中,与马丁·福勒(Martin Fowler)的其他前同事一起工作了数年之后,我开始注意到以这种方式编写的大多数好的类最终都只有一个doSomething方法。

不利的一面是,一旦意识到这只是一种将闭包作为类来编写闭包的长途OO方式,以便获得功能编程的好处,那么编写OO代码的动机就会迅速消失。

评论


基于构造函数的问题是,您总是需要添加更多的构造函数参数...

–米格尔·平
2011-2-10在11:24

哈哈。如果您做得太多,您很快就会发现您的代码很糟糕,您将对其进行重构。此外,ctrl-f6并不难。

–time4tea
2011-02-10 17:10

当然,反之亦然:这只是一种将类编写为闭包的笨拙的长函数函数方式,以便获得OO编程的好处

–CurtainDog
2011年2月11日,9:32

很多年前,我在Perl中使用闭包构建了一个对象系统,所以我明白了您的意思,但是封装数据并不是OO编程的独特优势,因此,尚不清楚OO的这些有益功能有哪些可比性笨拙而长远地获得功能语言。

– sanityinc
2011年2月11日下午13:34

#13 楼

我发现构造函数注入会导致庞大的丑陋构造函数,(并且我在代码库中都使用了它-也许我的对象太细粒度了?)。同样,有时使用构造函数注入会导致可怕的循环依赖关系(尽管这种情况非常罕见),因此您可能会发现自己必须拥有某种就绪状态生命周期,并且必须在更复杂的系统中进行几轮依赖注入。 >
但是,相对于setter注入,我更喜欢使用construtor注入,因为一旦构造了我的对象,我无疑就会知道它处于什么状态,无论是在单元测试环境中还是在某些IOC容器中加载。用一种a回曲折的方式,这就是说我感觉是二传手注射的主要弊端。

(作为旁注,我确实认为整个话题都非常“宗教化”,但是您的里程会开发团队中的技术狂热程度会有所不同!)

评论


如果您有丑陋的构造函数,也许您的类太庞大了,而您只需要依赖很多?

–罗伯特
10 Mar 9 '10 at 9:41



当然,这是我愿意接受的可能性!...可悲的是,我没有一支可以与我一起进行同行评审的团队。小提琴在背景中轻柔地弹奏,然后屏幕逐渐变黑。

–詹姆斯B
2010-3-9在9:48

马克·西曼(Mark Seeman)伤心欲绝,“这可能对您的职业生涯造成危险” ...

–罗伯特
2010-3-9在9:56

我没有任何线索背景叙述可以在这里表现得那么好:P

–阿努拉格
2010年3月9日10:10

@Robert我正在寻找报价,他在上面说过什么?

– liang
2015年6月2日14:43

#14 楼

如果您使用的是没有IOC容器的DI,那么最大的缺点就是您会很快看到您的代码实际具有多少依赖关系以及所有东西之间的紧密耦合程度。 (“但是我认为这是一个很好的设计!”)自然的过程是向IOC容器过渡,该容器可能需要一点时间来学习和实施(虽然不如WPF学习曲线那么糟糕,但是它不是免费的)要么)。最后的缺点是一些开发人员将开始编写诚实到善良的单元测试,这将需要他们花费一些时间来解决。以前可以在半天之内解决问题的开发人员会突然花两天的时间来弄清楚如何模拟所有依赖项。

类似于Mark Seemann的回答,最重要的是您要花时间成为一个更好的开发人员,而不是将一些代码一起乱搞然后扔出去/投入生产。您的公司宁愿拥有哪一家?只有你能回答。

评论


好点子。那就是质量差/交货快与质量差/交货慢。不足之处? DI不是魔术,期望它是弊端。

– liang
13年7月18日在15:33



#15 楼

DI是一种技术或模式,与任何框架都不相关。您可以手动连接依赖项。 DI帮助您实现SR(单一职责)和SoC(关注点分离)。 DI导致更好的设计。从我的观点和经验来看,没有缺点。像使用其他任何模式一样,您可能会出错或滥用它(但是在DI的情况下很难)。

如果您将DI作为原理引入到旧版应用程序中,则使用框架-您能做的最大的错误就是将其误用作服务定位器。 DI + Framework本身很棒,并且在我看到的任何地方都能使它变得更好!从组织的角度来看,每个新流程,新技术,新模式...都存在常见问题:


您必须训练团队
您必须更改应用程序(其中包括风险)

一般来说,您必须花费时间和金钱,除此之外,真的没有缺点!

#16 楼

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

评论


您不需要使用XML配置文件进行依赖项注入-选择一个支持代码中配置的DI容器。

–Håvard S
2010年3月9日在8:35

依赖注入不一定暗示XML文件。在Java中似乎仍然如此,但是在.NET中,我们早在几年前就放弃了这种耦合。

– Mark Seemann
2010年3月9日在8:35

或者根本不使用DI容器。

–杰西·米利坎(Jesse Millikan)
2010年3月9日在8:36

如果您知道代码正在使用DI,则可以轻松地假设谁设置了依赖项。

– Bozho
2010-3-9在8:49

例如,在Java中,Google的Guice不需要XML文件。

– Epaga
2011-2-14在7:54

#17 楼

两件事:


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

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

,这就是“蛋糕”模式(如Scala社区所知)是一种原因的原因之一。好主意:组件之间的接线可以通过类型检查器检查。使用注释或XML并没有带来好处。


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

像Spring或Guice这样的框架使它变得困难。静态确定容器创建的对象图的外观。尽管它们在容器启动时创建了一个对象图,但是它们没有提供有用的API,这些API描述了/ would /将要创建的对象图。

评论


这绝对是公牛。依赖注入是一个概念,它不需要一个笨拙的框架。您所需要的只是新的。抛开弹簧和所有这些废话,您的工具就可以正常工作,而且您将能够更好地重构代码。

–time4tea
2011-02-10 17:02

是的,很好。我应该更清楚地知道我在谈论DI框架的问题,而不是模式。回答时,我可能错过了对问题的澄清(假设当时在那儿)。

–马丁·埃利斯(Martin Ellis)
2011-02-14 17:39



啊。在这种情况下,我很高兴收回烦恼。道歉。

–time4tea
2011-02-14 17:59

#18 楼

当您不断采用技术解决静态类型问题时,静态类型语言的预期好处似乎已大大减少。我刚刚采访的一家大型Java商店正在通过静态代码分析来映射其构建依赖关系...必须解析所有Spring文件才能有效。

#19 楼

因为IoC容器应该以适当的方式解决依赖关系,并且有时需要进行多次迭代,所以它可以增加应用程序的启动时间。

评论


仅给出一个数字,一个DI容器应该每秒解决数千个依赖项。 (codinginstinct.com/2008/05/…)DI容器允许延迟实例化。性能(即使在大型应用程序中)也不应该成为问题。或至少性能问题应该得到解决,而不是决定是否违反IoC及其框架的理由。

–罗伯特
10 Mar 9 '10 at 9:12