35

我看到很多文章都在说 IoC 和 DI 有多棒,却没有说它为什么不那么棒,因为它会使代码变得更复杂。我还看到 IoC 不应该是代码的核心部分,而更多的是用于库和插件。文章通常很少提及这两种模式如何使代码变得更复杂,但没有太多关于细节的介绍。那是我的问题——你不应该在哪里使用这些模式?

这是一个很好的线程:什么是控制反转?. 如果你再往下看,有一篇关于拼写检查器的帖子和另一篇关于如果 IoC 只是一个拼写检查器的话,它可能不是很好用的帖子。作为一般准则,是否应该不使用 IoC,因为我曾经只有一个具体的接口类?意思是,我有 IMyClass。然后只有实现 IMyClass 的具体 MyClassA。为什么我想要 IoC?

如果我有 MyClassA、MyClassB 和 MyClassC,每个都实现了 IMyClass,那么这些可能是 IoC 的好候选人,对吗?

从同一个线程,有谁知道这篇文章的意思:

  • 控制反转=婚姻
  • 国际奥委会容器=妻子
4

8 回答 8

35

关于您关于只有一个接口实现的问题。当您使用 IoC 时,使用接口仍然很有用。使用这些接口的模拟来创建真正的单元测试(不依赖于接口实现是否正常工作)会容易得多。使用 IoC 的核心是让代码更容易测试。所以,如果你不想测试,或者已经有了更好的测试计划,就不要使用 IoC。

恕我直言,DI 和 IoC 复杂性的增加是通过更容易测试和更少耦合的代码来支付的。更容易隔离问题并进行未来更改。你也可以更好地控制它的行为。

我可以看到何时不使用 IoC 容器(因为它会导致配置开销)。这将发生在小型项目上,您可以手动完成,而不是使用容器。但是我看不出使用 DI 有多少损失,除非你不打算测试你的代码......

于 2009-09-01T15:11:36.283 回答
21

控制反转=婚姻

国际奥委会容器=妻子

婚姻是已知模式的定义——妻子是该模式的实现;-)

伯纳德。

于 2009-09-30T03:37:06.993 回答
15

是否使用 IoC 容器并不是在单个类级别做出的决定——在任何项目中,您都会拥有由容器创建和不由容器创建的类型。

复杂性权衡是这样的:

  • 对于少量组件,IoC 容器确实会增加一些开销
  • 随着应用程序中组件数量的增加:
    • 如果没有 IoC 容器,添加新组件或重构会变得越来越困难
    • 使用 IoC 容器,添加新组件或重构保持不变:您只需要担心正在添加或更改的组件的直接依赖关系。

如果您曾经经历过即使是精心设计和维护的代码库也随着大小变得难以管理的现象,那么您已经经历过 IoC 容器解决的问题。

于 2009-09-01T17:54:36.937 回答
11

来自城堡项目

为什么我不使用它?

如果您不熟悉这些概念并且没有意识到它们试图解决的问题,则不应使用控制反转容器。

此外,根据项目的规模和复杂性,IoC 容器可能会过大。更喜欢在大中型项目上使用它。

于 2009-09-01T15:08:37.973 回答
6

对 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 的基本假设是加载数据和循环遍历在某种程度上是困难的,需要被抽象掉;这个假设是不正确的,因为它永远不会超过几行(或者你可以将它移动到一个单独的函数中),即使它确实节省了代码,它也会降低你的灵活性。

于 2009-09-01T15:30:11.270 回答
4

IoC/DI 不是技术,它们是范例。您的问题类似于问“我什么时候不应该使用面向对象编程?” 这是一个比代码库的各个单元更大的决定。

要回答您的具体问题,如果您的类足够少,您可以在其中合理地手动构建对象图,并且您没有多次重复相同的对象图实例化,那么您可能不需要 IoC 容器。

于 2009-09-01T17:55:46.873 回答
2

我想我会说简单的答案只在您希望进行单元测试的对象上使用 IoC。

如果这看起来很轻率,我很抱歉,这是出于对编写代码的挫败感,而实际上没有考虑单元测试。我真的不明白为什么会增加复杂性——如果我需要知道实际的对象类型以确保我总是可以在运行时打印出类。

于 2009-09-01T18:00:03.743 回答
0

本文解决了您的问题“您不应该在哪些地方使用这些模式?” 并提供有关您的评论的详细示例,即“两种模式([IoC 和 DI])可以使代码更复杂”:

http://java.dzone.com/articles/beyond-injection-concerns-ioc

总结一下这篇文章的两点:

  1. 你不应该在封装很重要的地方使用 IoC/DI。
  2. 如果无法证明通过使用 IoC/DI 增加的复杂性如何被使用 IoC/DI 的好处所抵消,则不应使用 IoC/DI。
于 2013-08-12T15:40:11.723 回答