什么时候不使用 IoC 和 DI? [关闭]
Posted
技术标签:
【中文标题】什么时候不使用 IoC 和 DI? [关闭]【英文标题】:When not to use IoC and DI? [closed] 【发布时间】:2010-11-24 16:18:34 【问题描述】:我看到很多文章都在说 IoC 和 DI 有多么出色,却没有提到为什么它不那么出色,因为它会使代码变得更复杂。我还看到 IoC 不应该是代码的核心部分,而更多的是用于库和插件。文章通常很少提及这两种模式如何使代码变得更复杂,但没有太多关于细节的介绍。那是我的问题 - 您具体不应该在哪些地方使用这些模式?
这是一个不错的主题:What is Inversion of Control?。如果你再往下看,有一篇关于拼写检查器的帖子和另一篇关于如果 IoC 只有一个拼写检查器的话,它可能不是很好用的帖子。作为一般准则,是否应该不使用 IoC,因为我曾经只有一个具体的接口类?意思是,我有 IMyClass。然后只有实现 IMyClass 的具体 MyClassA。为什么我需要 IoC?
如果我有 MyClassA、MyClassB 和 MyClassC,它们都实现了 IMyClass,那么它们可能是 IoC 的良好候选者,对吗?
来自同一个帖子,有谁知道这篇文章的意思:
控制反转 = 婚姻 IOC 容器 = 妻子【问题讨论】:
如果你还没看过,我也推荐Martin Fowler的IoC/DI文章:martinfowler.com/articles/injection.html 我读过,对我的问题没用。 也许婚姻/妻子的类比是“妻子是婚姻的一种具体实现”? :) 有各种 IOC 容器,但控制反转是一个概念 【参考方案1】:关于您关于只有一个接口实现的问题。使用 IoC 时,使用接口仍然很有用。使用这些接口的模拟来创建真正的单元测试(不依赖于接口实现是否正常工作)会容易得多。使用 IoC 的核心是使代码更易于测试。因此,如果您不想测试,或者已经有了更好的测试计划,请不要使用 IoC。
恕我直言,DI 和 IoC 复杂性的增加是通过更容易测试和更少耦合的代码来支付的。更容易隔离问题并进行未来的更改。你也可以更好地控制它的行为。
我可以看到何时不使用 IoC 容器(因为它会导致配置开销)。这将发生在小型项目上,您可以手动完成,而不是使用容器。但是我看不出使用 DI 有多少损失,除非你不打算测试你的代码......
【讨论】:
改进的设计重构不会消除对 IoC 和 DI 的需求吗?我问是因为 IoC 和 DI 根本没有表现力(即可读性)。如果可以重构代码以创建 IoC 努力实现的可读性和独立性,我相信您最终会得到更好的解决方案。不过,如果不使用 IoC 和 DI,您可能仍然无法完成模拟测试部分。 恕我直言,IoC 和 DI 提高了可读性,一旦我知道对象需要什么。如果你的构造函数有太多的参数,你可能在那个类里面做的太多了,你应该重构 此外,除 Java 之外的大多数编程语言都有“命名 initargs”,进一步减少了 DI 样式可能引入的任何可读性问题。【参考方案2】:控制反转 = 婚姻
IOC 容器 = 妻子
婚姻是已知模式的定义——妻子是该模式的实现;-)
伯纳德。
【讨论】:
这不是很准确。 Ioc Container 是一个牧师,他与(结婚)2 个对象(人)。【参考方案3】:是否使用 IoC 容器并不是在单个类级别做出的决定 - 在任何项目中,您都会拥有由容器创建和不由容器创建的类型。
复杂性权衡是这样的:
对于少量组件,IoC 容器确实会增加一些开销 随着应用程序中组件数量的增加: 如果没有 IoC 容器,添加新组件或重构会变得越来越困难 使用 IoC 容器,添加新组件或重构保持不变:您只需要担心正在添加或更改的组件的直接依赖关系。如果您曾经经历过即使是精心设计和维护的代码库也随着大小而变得难以管理的现象,那么您已经经历过 IoC 容器解决的问题。
【讨论】:
+1 刚刚发现自己在问这个确切的问题的情况下,因为我看不到使用我的 IoC 容器(这是 AutoFac,所以谢谢!:) 对于这个特定的场景,而在该项目的其他部分,IoC 似乎是一个显而易见的选择,因为它的好处是显而易见的。【参考方案4】:来自the Castle Project:
我为什么不使用它?
你不应该使用反转 如果不是,请控制容器 熟悉这些概念,如果你 没有意识到他们尝试的问题 解决。
另外,取决于大小和 项目的复杂性,IoC 容器可能是矫枉过正。较喜欢 在大中型项目中使用它。
【讨论】:
-1 "如果您不熟悉这些概念并且没有意识到它们试图解决的问题,则不应使用 ???。"这是一个重言式,因为它适用于任何???。 +1 第二个原因很好。 #1 确实比其他任何东西都更符合常识,但却是一个简单而认真的“你知道自己在做什么吗?”查询是您可以做的最简单的代码审查。 小应用程序往往会变成大应用程序。当你把你的小应用程序写成一个小的应用程序并且它变成一个大应用程序时,你会变得一团糟。当你编写你的小应用程序时,它就像一个大应用程序,它变成了一个大应用程序,一切正常。唯一的缺点是不得不听人们抱怨“过度工程”。 @jrockway -- 这也是我的经验。一个好的 IoC 容器的好处在于,一个大项目可以看起来很小。 @4thSpace -- 我会说不,但无论如何你都应该小心使用静态类。它们变得“方便”,然后成为上帝的对象,然后无法测试,然后成为问题的一大来源。【参考方案5】:关于 IoC 的抱怨很容易理解:IoC 将一些简单的事情变成了复杂的事情。假设你想对列表的每个元素做一些事情(在伪代码中):
for each i in list:
do_something(i)
IoC 本质上是将循环迭代的责任转移给其他人。因此,我们最终得到的结果更像这样:
ioc = new IocContainer()
ioc.register_callback(do_something)
ioc.go()
请注意,即使是这种简单的形式,代码也比原来的要长...这还不包括 IocContainer 的实现。
然后,以最奇特的形式,IocContainer 可以静态初始化,也可以通过静态 Main() 函数或其他隐藏的地方进行初始化,并且 register_callback() 函数会根据其他一些遍历 XML 文件的代码自动调用它列出了你所有的 do_something() 函数,(有时)甚至列出了要迭代的列表的内容。
是的,XML 文件使您的软件更具可配置性!正确的?只需更改一个简单的文本文件,您就可以更改列表中已完成的操作!
具有讽刺意味的是,您的源代码现在更长并且更难理解,它依赖于各种新的、可能有错误的库(包括 IocContainer 和 XML 解析器),而且您实际上,它更难维护:XML 代码更难理解和编辑原始的简单源代码循环(无论循环是用什么语言编写的)。由于 IocContainer 将您的责任从您手中移开,因此您对如何进行迭代(排序或未排序?深度优先还是广度优先?)的控制也较少。
对于插件之类的东西,使用 IoC 是有意义的,因为主应用程序控制执行过程并在这里和那里向插件寻求帮助是合乎逻辑的 .这被称为“提供挂钩”,并且比 IoC 术语存在的时间要长得多。
对于像单元测试这样的事情(我经常看到它到处乱扔),IoC 通常证明是无用的,因为您的测试需要能够模拟各种奇怪的情况,而 IoC 只是不断地妨碍了这一点。
IoC 的基本假设是加载数据和循环遍历在某种程度上是困难的,需要抽象出来;这个假设是不正确的,因为它永远不会超过几行(或者你可以将它移动到一个单独的函数中),即使它确实节省了代码,它也会降低你的灵活性。
【讨论】:
我不会写一篇长文来反驳这一点,但基本上,我不同意。你的第一个例子是正交的; “对于列表中的每个 i”应该重写为函子上的 fmap。这与依赖注入无关。至于 DI,我发现它使我的应用程序的“重大”更改变得更加容易。如果“更下方”的某些东西需要一条新数据,我不必在“中间”重组一堆代码。它让我的对象图构建器处理细节,并使我的代码更加模块化。都不错! "...我的对象图生成器..." .NET 中有什么对象图的好例子吗?【参考方案6】:IoC/DI 不是技术,它们是范例。您的问题类似于问“我什么时候不应该使用面向对象编程?”这是一个比代码库的单个单元更大的决定。
要回答您的具体问题,如果您可以合理地手动构建对象图的类足够少,并且您不会多次重复相同的对象图实例化,那么您可能不需要 IoC 容器。
【讨论】:
你能解释一下对象图吗? 对象图 (en.wikipedia.org/wiki/Object_graph) 描述了运行时实例之间的关系。例如,假设FooService
有一个IBarRepository
作为构造函数依赖项,而您使用BarRepository
的实例来实现它。然后对象图将是FooService0 -> BarRepository0
。由于 IoC 容器满足所有依赖关系,因此可以说它构建了对象图。【参考方案7】:
我想我会说简单的答案只在您希望进行单元测试的对象上使用 IoC。
如果这看起来很轻率,我很抱歉,这是出于对编写代码的挫败感,而实际上没有考虑单元测试。我真的不明白为什么会增加复杂性——如果我需要知道实际的对象类型以确保我总是可以在运行时打印出类。
【讨论】:
【参考方案8】:本文解决了您的问题“您不应该在哪些地方使用这些模式?”并就您的评论给出了详细的示例,即“两种模式([IoC 和 DI])可以使代码更复杂”:
http://java.dzone.com/articles/beyond-injection-concerns-ioc。
总结本文的两点:
-
不应在封装很重要的地方使用 IoC/DI。
您不应该使用 IoC/DI,因为您无法展示通过使用 IoC/DI 增加的复杂性如何被使用 IoC/DI 的好处所抵消的具体示例。
【讨论】:
以上是关于什么时候不使用 IoC 和 DI? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章