关于设计模式:何时不使用IOC和DI?

When not to use IoC and DI?

我看到很多文章说IOC和DI有多伟大,但却没有提到为什么它不那么伟大,因为它可以使代码更加复杂。我还看到IOC不应该是代码的核心部分,而应该更多地用于库和插件。文章通常只是对这两种模式如何使代码变得更复杂的一个小参考,但并没有详细介绍。这是我的问题——你在哪里不应该使用这些模式?

这是一条很好的线索:什么是控制反转?如果你再往下看,有一篇关于拼写检查的文章,还有一篇关于如果只有一个拼写检查,那么IOC在这里可能不是一个好的用途。作为一个总的指导方针,IOC不应该被用于我只有一个具体的接口类吗?意思是,我有我的屁股。然后有一个具体的myclassa来实现imyclass。为什么我要国际奥委会在那里?

如果我有我的班级,我的班级,我的班级,我的班级,我的班级,我的班级,我的班级,我的班级,我的班级,我的班级,我的班级,我的班级,我的班级,我的班级,我的班级,我的班级,我的班级,我的班级,我的班级,我的班级

从同一条线索来看,是否有人知道这篇文章的含义:

  • 控制权倒置=婚姻
  • IOC集装箱=妻子


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

IMHO、DI和IOC的复杂性增加是通过更容易测试和更少耦合的代码来实现的。隔离问题和做出未来的改变更容易。你也可以更好地控制它的行为。

我可以看到何时不使用IOC容器(因为它会导致配置开销)。这将发生在小型项目上,您可以在那里手动执行,而不是使用容器。但我看不出使用DI会有什么损失,除非您不打算测试代码…


控制权倒置=婚姻

IOC集装箱=妻子

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

伯纳德。


不管是否使用IOC容器,都不是在单个类的层次上做出的决定——在任何项目中,您都将拥有容器创建的类型和非容器创建的类型。

复杂性权衡如下:

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

如果您曾经经历过这样的现象:即使是设计良好且维护良好的代码库,也会随着大小而变得无法管理,那么您也会遇到IOC容器解决的问题。


城堡项目:

Why would I not use it?

You should not use an Inversion of
Control container if you are not
familiar with the concepts and if you
do not realize the problems they try
to solve.

Also, depending on the size and
complexity of the project, an IoC
container might be overkill. Prefer to
use it on medium to large projects.


关于国际奥委会的抱怨很容易理解:国际奥委会把简单的事情变成复杂的事情。假设您想对列表中的每个元素(伪代码)执行某些操作:

1
2
for each i in list:
    do_something(i)

实际上,IOC将循环迭代的责任转移到了其他人身上。因此,我们最终得到了类似的结果:

1
2
3
ioc = new IocContainer()
ioc.register_callback(do_something)
ioc.go()

注意,即使在这个简单的形式中,代码也比原来的长…这不包括ioccontainer的实现。

然后,在其最华丽的形式中,ioccontainer可以被静态初始化,或者被一个静态的main()函数初始化,或者被其他隐藏的地方初始化,然后根据其他一些代码自动调用register_callback()函数,这些代码迭代一个XML文件,其中列出了所有的do_something()函数,甚至(有时)列出了lis的内容。要迭代的TS。

是的,XML文件使您的软件更加可配置!正确的?只需更改一个简单的文本文件,就可以更改对列表执行的某些操作!

但是,具有讽刺意味的是,您的源代码现在越来越难理解,它依赖于各种新的、可能有缺陷的库(包括ioccontainer和XML解析器),实际上您已经使其难以维护:XML代码比原始的简单源代码循环(无论循环使用何种语言)更难理解和编辑被写入)。您对迭代(排序的还是未排序的)的确切方式也没有太多控制权。深度优先还是宽度优先?)因为ioccontainer在承担你的责任。

对于插件之类的东西,使用IOC是有意义的,因为主应用程序控制执行过程是合乎逻辑的,只需在这里或那里向插件寻求帮助。这被称为"提供挂钩",比国际奥委会的术语要长得多。

对于单元测试(这是我经常看到的情况),IOC通常是没有帮助的,因为您的测试需要能够模拟各种各样的奇怪情况,而IOC只是经常遇到这种情况。

IOC的基本假设是加载数据和循环在某种程度上是困难的,需要排除;这个假设是不正确的,因为无论如何它都不超过几行(或者您可以将其移动到一个单独的函数),即使它确实保存了代码,它也会降低您的灵活性。


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

为了回答您的特定问题,如果您有足够多的类可以手工合理地构造对象图,并且您没有多次重复相同的对象图实例化,那么您可能不需要IOC容器。


我想我会说这个简单的答案只在你想要单元测试的对象上使用IOC。

如果这看起来是轻率的话,我很抱歉,这是出于对编写代码的失望,实际上没有考虑单元测试。我真的不明白为什么会有额外的复杂性——如果我需要知道实际的对象类型,那么我肯定可以在运行时打印出类。


本文解决了您的问题"您在哪里不应该使用这些模式?"并给出了关于"这两种模式([ioc和di])会使代码更加复杂"的详细示例:

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

总结本文的两点:

  • 在封装很重要的地方,不应该使用IOC/DI。
  • 在无法演示使用IOC/DI增加的复杂性如何被使用IOC/DI带来的好处权衡的具体示例的情况下,不应使用IOC/DI。