反对控制反转容器的论据

Posted

技术标签:

【中文标题】反对控制反转容器的论据【英文标题】:Arguments against Inversion of Control containers 【发布时间】:2011-08-05 18:49:53 【问题描述】:

似乎每个人都在转向 IoC 容器。我已经尝试“摸索”了一段时间,尽管我不想成为一个在高速公路上走错路的司机,但它仍然没有通过常识对我的考验。让我解释一下,如果我的论点有缺陷,请纠正/启发我:

我的理解:当组合不同的组件时,IoC 容器应该让您的生活更轻松。这是通过 a) 构造函数注入、b) setter 注入和 c) 接口注入来完成的。然后以编程方式或在容器读取的文件中“连接”它们。然后按名称召唤组件,然后在需要时手动施放。

我没有得到什么:

编辑(更好的措辞) 如果组件设计得当(使用 IoC 模式、松散耦合),当您可以(恕我直言)更清晰地“连接”应用程序时,为什么要使用不符合语言习惯的不透明容器?这个“托管代码”如何获得重要的功能? (我听说过一些关于生命周期管理的内容,但我不一定明白这比自己动手做的更好/更快。)

原创: 为什么要竭尽全力将组件存储在容器中,以不符合语言习惯的方式“连接它们”,在按名称调用组件时使用相当于“goto 标签”的东西,然后丢失很多通过手动转换获得静态类型语言的安全优势,当您通过不这样做来获得 等效 功能时,而是使用现代 OO 语言提供的所有很酷的抽象特性,例如编程到接口?我的意思是,实际需要使用手头组件的部分必须知道他们在任何情况下都在使用它,在这里您将使用最自然、最惯用的方式进行“接线” - 编程!

【问题讨论】:

【参考方案1】:

我确信关于这个主题有很多话要说,希望我会编辑这个答案以便稍后添加更多内容(希望更多的人会添加更多答案和见解),但只是几个简短的要点发布...

使用 IoC 容器是 inversion of control 的一个子集,而不是全部。您可以将控制反转用作设计构造,而无需依赖 IoC 容器框架。在这种情况下,最简单的控制反转可以表述为“供应,不要实例化”。只要您的对象在内部不依赖于其他对象的实现,而是要求向它们提供实例化的实现,那么您就使用了控制反转。即使您没有使用 IoC 容器框架。

关于对接口进行编程的观点...我不确定您对 IoC 容器的体验如何(我个人最喜欢的是 StructureMap),但您肯定对 IoC 接口进行了编程。整个想法,至少在我使用它的方式上,是你将你的接口(你的类型)与你的实现(你注入的类)分开。依赖接口的代码只针对这些接口进行编程,并在需要时注入这些接口的实现。

例如,您可以有一个IFooRepository,它从Foo 类型的数据存储实例返回。所有需要这些实例的代码都从提供的IFooRepository 类型的对象中获取它们。在其他地方,您创建FooRepository 的实现并配置您的IoC 以在任何需要IFooRepository 的地方提供它。这个实现可以从数据库、XML 文件、外部服务等中获取它们。不管在哪里。该控制已被颠倒。您使用 Foo 类型对象的代码并不关心它们来自哪里。

明显的好处是您可以随时更换该实现。您可以将其替换为测试版本,根据环境更改版本等。但请记住,您也不需要在任何给定时间拥有如此 1 比 1 的接口与实现比率。

例如,我曾经在之前的工作中使用过一个代码生成工具,它将大量的 DAL 代码生成到一个类中。把它拆开会很痛苦,但是配置它以特定的方法/属性名称将其全部吐出并不是很痛苦。因此,我为我的存储库编写了一堆接口,并生成了一个实现了 all 的类。对于那个生成的类,它很难看。但是我的应用程序的其余部分并不关心,因为它将每个接口都视为自己的类型。 IoC 容器只是为每个容器提供了相同的类。

我们能够通过这个快速启动并运行,没有人在等待 DAL 的开发。当我们继续在使用接口的域代码中工作时,初级开发人员的任务是创建更好的实现。这些实现后来被换了,一切都很好。

正如我之前提到的,这一切都可以在没有 IoC 容器框架的情况下完成。真正重要的是模式本身。

【讨论】:

感谢您的回答大卫!我同意使用 IoC 模式很重要,并且可以创建更出色的 API。我的“牛肉”是使用框架和容器——我只是看不到存储对象实例和“连接”你的应用程序的意义,这对你来说几乎是一个不透明的虚拟机。 【参考方案2】:

肯定有people who think that DI Containers add no benefit,问题成立。如果纯粹从对象组合的角度来看,容器的好处似乎可以忽略不计。任何第三方都可以连接松散耦合的组件。

但是,一旦您超越了玩具场景,您应该意识到连接合作者的第三方必须承担更多的简单责任,而不是简单的组合。还可能存在退役问题以防止资源泄漏。由于作曲家是唯一知道给定实例是共享还是私有的一方,因此它还必须承担生命周期管理的角色。

当您开始组合各种实例范围、使用共享服务和私有服务的组合,甚至可能将某些服务的范围限定为特定上下文(例如 Web 请求)时,事情就会变得复杂。用穷人的 DI 编写所有代码当然是可能的,但它不会增加任何商业价值——它是纯粹的基础设施。

此类基础架构代码构成通用子域,因此创建可重用库来解决此类问题是很自然的。这正是 DI 容器。

顺便说一句,我知道的大多数容器不使用名称来连接自己 - 它们使用 自动连接,它将来自构造函数注入的静态信息与容器从接口到具体类的映射配置相结合.简而言之,容器本身就能够理解这些模式。

DI 不需要 DI 容器 - 它非常有用。


更详细的处理可以看文章When to use a DI Container。

【讨论】:

感谢您的回答马克。好的,我同意应该将通用功能抽象并制成可重用的组件。是的,一些容器确实让你的生活更轻松(对我来说最值得注意的是:应用程序服务器。从你的回复来看,它们似乎与 IoC 容器足够相似)。我还没有听说过自动接线(可能是因为它不在教程和初学者文章中......)也许我需要了解更多关于它们的信息,但在那之前我会保持怀疑:) @vivri:你的平台是什么? 。网?爪哇?还有什么? 目前我生活在 Java 生态系统中,我们在工作的地方使用 Spring,但是 (a) 显然我们没有充分利用它(我们使用 EJB,显然已经被 Spring 淘汰了) , 和 (b) 我还没有接触过任何需要使用它的东西。 好的。我最熟悉 .NET 容器。平台之间存在差异,但 Spring.NET 支持自动装配,尽管程度与其他 .NET 容器不同。 我猜这些差异与 .NET 对 lambda 的支持有关。【参考方案3】:

首先什么是国际奥委会?这意味着创建依赖对象的责任从主对象中分离出来并委托给第三方框架。我总是使用 spring 作为我的 IOC 框架,它为我带来了很多好处。

    促进编码到接口和解耦 - 主要好处是 IOC 促进和使解耦变得非常容易。您始终可以在主对象中注入接口,然后使用接口方法执行任务。主对象不需要知道将哪个依赖对象分配给接口。当您想使用不同的类作为依赖项时,您只需在配置文件中将旧类与新类交换,而无需更改任何一行代码。现在您可以争辩说,这可以使用各种界面设计模式在代码中完成。但 IOC 框架却是游刃有余。因此,即使是新手,您也会成为利用各种界面设计模式(如桥接、工厂等)的专家。

    干净的代码 - 由于大多数对象创建和对象生命周期操作都委托给 IOC 容器,因此您可以从编写肉鸡点重复代码中节省下来。所以你有一个更干净、更小、更容易理解的代码。

    单元测试 - IOC 使单元测试变得容易。由于剩下的是解耦代码,因此您可以轻松地单独测试解耦代码。您还可以轻松地在测试用例中注入依赖项,并查看不同组件如何交互。

    属性配置器 - 几乎所有应用程序都有一些属性文件,用于存储应用程序特定的静态属性。现在要访问这些属性,开发人员需要编写包装器来读取和解析属性文件,并以应用程序可以访问的格式存储属性。现在所有的 IOC 框架都提供了一种在特定类中注入静态属性/值的方法。所以这又变成了在公园里散步。

这些是我能马上想到的一些要点,我相信还有更多。

【讨论】:

以上是关于反对控制反转容器的论据的主要内容,如果未能解决你的问题,请参考以下文章

Java面试和开发中的IoC(控制反转)

Spring 学习笔记控制反转 IoC

控制反转和依赖注入的关系总结

Spring中的IoC(控制反转)具体是什么东西

php容器 -- 控制反转 -- 依赖注入

大话DI依赖注入+IOC控制反转 之 定义