使用依赖注入的缺点是什么?[关闭]


336

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

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


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


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


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

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

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

2
@kissaki与二传手相距甚远。最好制作可用于建筑的物体。如果你不相信我,只是尽量增加另一个合作者与“二传手”注入一个对象,你会得到肯定....一个NPE
time4tea

Answers:


209

要点:

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

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


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

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

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

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

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

183

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

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

关键是编写可以完成工作并且健壮,易读和可维护的代码。您是软件开发人员,而不是象牙塔的构建者。

相关连结

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

对于原始开发人员而言,这些“依赖关系”不只是一个问题-使用该接口的每个人都必须查找依赖关系是什么,如何指定依赖关系以及它们的含义,并弄清楚如何为应用程序做些什么。在这里,“明智的违约”可以使生活变得更加简单。

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

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


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

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

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

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

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

77

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

  • 学习需要时间
  • 如果被误解,可能导致弊大于利
  • 如果走到了极点,那么可能会付出更多的工作,而不是证明好处的理由

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

8
考虑到您不想进行主观讨论,这似乎有些花招。我建议(如果需要的话,总的来说)建立一个现实的例子进行检查。建立没有 DI 的样本将是一个很好的起点。然后,我们可以挑战需求变更并检查其影响。(顺便提一下,这对我建议睡觉非常方便。)
Jesse Millikan 2010年

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

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

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

45

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


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

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

1
我同意(我什至投票赞成你)。我只是在说清楚。
维多利亚

1
@vickirk:我想它的缺点是人类很难理解。从人类难以理解的角度上讲,去耦事物会增加复杂性,并且需要更长的时间才能完全理解。
Bjarke Freund-Hansen 2010年

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

42

7
我很好奇,当有人要钱时,为什么会本着“没有任何东西”的精神来回答,而那些包含与该问题有关的信息的回答却没有呢?
加布里埃尔·Ščerbák,2010年

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

4
我不知道最喜欢的答案是否也被否决了,因为它真的根本无法回答这个问题,恕我直言。答案无济于事,而显然说DI的缺点是它太酷了,这很有帮助。
加布里埃尔·Ščerbák2010年

41

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

  • 代码可能变得更难以理解。依赖注入可以以非常有创意的方式使用。例如,我刚遇到了一些代码,这些代码使用自定义批注注入某些IOStreams(例如:@ Server1Stream,@ Server2Stream)。尽管这确实有效,并且我承认具有一定的优雅性,但它使理解Guice注入成为理解代码的先决条件。
  • 学习项目时学习曲线较高。这与第1点有关。为了了解使用依赖注入的项目的工作方式,您需要了解依赖注入模式和特定框架。当我开始目前的工​​作时,我花了很多时间来迷惑Guice在幕后所做的事情。
  • 构造函数变大。尽管可以使用默认构造函数或工厂在很大程度上解决此问题。
  • 错误可以被混淆。我最近的例子是我在两个标志名称上发生了冲突。Guice默默地吞下了错误,并且我的标志之一未初始化。
  • 错误被推送到运行时。如果您错误地配置了Guice模块(循环引用,绑定错误,...),则在编译时不会发现大多数错误。而是在程序实际运行时暴露错误。

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

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


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

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

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

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

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

24

没有任何DI的代码会陷入陷入Spaghetti代码的众所周知的风险-有些症状是类和方法太大,做得太多并且不容易更改,分解,重构或测试。

经常使用DI的代码可以是Ravioli代码,其中每个小类都像一个单独的馄饨块-它只做一件小事情,并且遵循单一职责原则,这很好。但是,单独查看类很难看到系统整体的功能,因为这取决于所有这些小部分如何组合在一起,这很难看到。看起来就像一大堆小东西。

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

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


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

13

如果您有自己开发的解决方案,则在构造函数中,依赖关系就在您的面前。或作为方法参数,也不太难发现。虽然框架管理的依赖项,如果放到了极点,可能会开始看起来像魔术。

但是,在太多的类中有太多的依赖关系是一个清晰的信号,表明您的类结构已经搞砸了。因此,以某种方式进行依赖项注入(本地或框架管理)可以帮助解决明显的设计问题,否则这些问题可能会被隐藏在黑暗中。


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

假设您要设计一个大学校园。您必须将某些设计委托给学生和教授,否则物理大楼对物理人员来说将无法正常工作。没有任何一位建筑师对物理学家需要自己做的事情了解得足够多。但是您不能将每个房间的设计委托给它的占用者,因为那样您会得到一堆巨大的瓦砾。

您如何在整个层次结构的所有层次上分配设计职责,同时又保持整体设计的一致性和协调性?这是Alexander试图解决的体系结构设计问题,但这也是计算机系统开发的基本问题。

DI解决了这个问题吗?没有。但这确实有助于您清楚地知道是否要将设计每个房间的责任委托给它的居住者。


13

有一两件事让我不安与DI一点是假设所有注入的对象是便宜的实例化不产生副作用的依赖,使用非常频繁,它胜过任何相关的实例化成本-或- 。

当消费类中不经常使用依赖项时,这可能很重要。例如诸如此类的东西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>实例作为依赖项。这将改变您的从属对象的构造函数,从而使您更加意识到实现细节,例如对象构造费用,这当然也是不希望的。


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

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

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


11

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

具体来说,如果您按住Control键单击/ Command键单击代码中的方法调用,它将带您进入接口上的方法声明,而不是具体的实现。

这实际上是松散耦合代码(由接口设计的代码)的缺点,并且即使您不使用依赖项注入(即,即使您仅使用工厂)也适用。但是依赖注入的出现才真正促使了将松散耦合的代码带给大众,因此我想我会提到它。

另外,松耦合代码的好处远不止于此,因此我将其称为nitpick。尽管我已经工作了很长时间,但是如果尝试引入依赖项注入,您可能会遇到这种回退。

实际上,我敢冒险猜测,对于依赖注入,您可以找到的每一个“缺点”,您都会发现许多远远超过它的优点。


5
Ctrl-shift-B和
resharper

1
但是话又说回来,我们(在理想的世界中)不会至少拥有所有事物的两种实现,即至少一种真正的实现和用于单元测试的模拟实现;-)
vickirk 2010年

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

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

10

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

因此,主要的缺点是,一旦您意识到这仅仅是一种将类作为对象编写闭包的笨拙的长途OO方法,以便获得功能编程的好处,那么编写OO代码的动力就会迅速消失。


1
基于构造函数的问题是,您总是需要添加更多的构造函数args
Miguel Ping

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

当然,反之亦然:为了获得OO编程的好处,这仅仅是一种将类作为闭包编写的
笨拙的

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

9

我发现构造函数注入会导致庞大的丑陋构造函数,(并且在整个代码库中都使用它-也许我的对象太细粒度了?)。另外,有时使用构造函数注入会导致可怕的循环依赖关系(尽管这种情况非常罕见),因此您可能会发现自己必须拥有某种就绪状态生命周期,并且在更复杂的系统中需要进行几轮依赖关系注入。

但是,相对于setter注入,我更喜欢使用construtor注入,因为一旦构造了我的对象,那么我无疑会知道它处于什么状态,无论是在单元测试环境中还是在某些IOC容器中加载。这以一种about回的方式在说,我认为是二传手注射的主要缺点。

(作为一个旁注,我确实发现整个主题都非常“虔诚”,但是您的努力程度会随开发团队中技术狂热程度的变化而变化!)


8
如果您有笨拙的构造函数,也许您的类太庞大了,而您只需要依赖很多?
罗伯特2010年

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

1
马克·西曼(Mark Seeman)伤心欲绝:“这可能对您的职业生涯有害”……
罗伯特2010年

我没有线索背景叙述可以在这里这么表达:P
Anurag,2010年

@Robert我想找到报价,他在上面说了什么?
liang 2015年

8

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

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


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

5

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

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

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

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


3

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


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

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

6
或者根本不使用DI容器。
杰西·米利坎

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

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

2

两件事情:

  • 他们需要额外的工具支持,以检查配置是否有效。

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

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

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

诸如Spring或Guice之类的框架使得难以静态确定容器创建的对象图的外观。尽管它们在启动容器时创建了一个对象图,但是它们没有提供描述/ would /创建的对象图的有用API。


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

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

啊。在这种情况下,我很乐意收回烦恼。道歉。
time4tea 2011年

2

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


1

由于IoC容器应以适当的方式解决依赖关系,因此有时可能需要进行多次迭代,因此可能会增加应用程序的启动时间。


3
仅给出一个数字,一个DI容器应该每秒解决数千个依赖项。(encodinginstinct.com/2008/05/…)DI容器允许延迟实例化。性能(即使在大型应用程序中)也不应该成为问题。或者至少性能问题应该是可以解决的,而不是决定是否违反IoC及其框架的理由。
罗伯特2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.