什么是控制反转?

控制反转(IoC)在第一次遇到时可能会非常混乱。

  1. 这是什么?
  2. 它解决了什么问题?
  3. 什么时候适合使用,什么时候不适合?
658297 次浏览
  1. 维基百科文章。对我来说,控制反转就是将按顺序编写的代码转换为委托结构。您的程序不是显式控制所有内容,而是设置一个类或库,其中包含某些函数,以便在某些事情发生时调用。

  2. 它解决了代码重复的问题。例如,在过去,你会手动编写自己的事件循环,在系统库中轮询新事件。如今,大多数现代API你只需告诉系统库你对什么事件感兴趣,它会告诉你什么时候发生。

  3. 控制反转是一种减少代码重复的实用方法,如果你发现自己复制了整个方法并且只更改了一小部分代码,你可以考虑使用控制反转来解决它。在许多语言中,通过委托、接口甚至原始函数指针的概念,控制反转变得很容易。

    它并不适合在所有情况下使用,因为以这种方式编写程序时可能更难遵循程序的流程。在编写可重用的库时,这是一种设计方法的有用方法,但应在您自己的程序核心中谨慎使用,除非它真的解决了代码重复问题。

我同意空对象,但我想补充一下:

如果您发现自己复制了整个方法并且只更改了一小部分代码,您可以考虑使用控制反转来解决它

如果你发现自己复制和粘贴代码,你几乎总是做错东西。编码为设计原则一次且只有一次

控制反转是您在程序回调时得到的,例如像gui程序。

例如,在旧的学校菜单中,您可能有:

print "enter your name"read nameprint "enter your address"read addressetc...store in database

从而控制用户交互的流程。

在GUI程序或其他程序中,我们会说:

when the user types in field a, store it in NAMEwhen the user types in field b, store it in ADDRESSwhen the user clicks the save button, call StoreInDatabase

所以现在控制是反转的……而不是计算机以固定的顺序接受用户输入,用户控制输入数据的顺序,以及数据何时保存在数据库中。

基本上,带有事件循环、回调或执行触发器的任何属于这一类。

#0(IOC)模式是关于提供callback中的任何形式(控制反应),而不是直接行动(换句话说,反转和/或将控制重定向到外部处理程序/控制器)。#2(DI)模式是IoC模式的更具体版本,完全是为了从代码中删除依赖项。

每个DI实现都可以被认为是IoC,但不应该称之为IoC,因为实现依赖注入比回调更难(不要使用通用术语“IoC”来降低产品的价值)。

对于DI示例,假设您的应用程序有一个文本编辑器组件,并且您想提供拼写检查。您的标准代码如下所示:

public class TextEditor {
private SpellChecker checker;
public TextEditor() {this.checker = new SpellChecker();}}

我们在这里所做的创建了TextEditorSpellChecker之间的依赖关系。在IoC场景中,我们会这样做:

public class TextEditor {
private IocSpellChecker checker;
public TextEditor(IocSpellChecker checker) {this.checker = checker;}}

在第一个代码示例中,我们实例化了SpellCheckerthis.checker = new SpellChecker();),这意味着TextEditor类直接依赖于SpellChecker类。

在第二个代码示例中,我们通过在TextEditor的构造函数签名中具有SpellChecker依赖类来创建抽象(而不是在类中初始化依赖)。这允许我们调用依赖项,然后将其传递给TextEditor类,如下所示:

SpellChecker sc = new SpellChecker(); // dependencyTextEditor textEditor = new TextEditor(sc);

现在,创建TextEditor类的客户端可以控制使用哪个SpellChecker实现,因为我们将依赖项注入TextEditor签名中。

但我认为你必须非常小心。如果你过度使用这种模式,你会做出非常复杂的设计,甚至更复杂的代码。

就像这个使用TextEditor的例子一样:如果你只有一个SpellChecker,也许真的没有必要使用IoC?除非你需要编写单元测试或其他东西…

无论如何:要合理。设计模式是良好做法,但不是要宣扬的圣经。不要到处贴。

  1. 控制反转是一种用于解耦系统中组件和层的模式。该模式是通过在构建组件时将依赖注入到组件中来实现的。这些依赖通常作为进一步解耦和支持可测试性的接口提供。IoC/DI容器,如Castle Windsor、Unity是可用于提供IoC的工具(库)。这些工具提供了简单依赖管理之外的扩展功能,包括生命周期、AOP/拦截、策略等。

  2. a.减轻组件负责管理其依赖项的责任。
    b.提供在不同环境中交换依赖实现的能力。
    c.允许通过模拟依赖项来测试组件。
    d.提供在整个应用程序中共享资源的机制。

  3. a.在进行测试驱动开发时至关重要。没有IoC,很难进行测试,因为被测组件与系统的其余部分高度耦合。
    b.在开发模块化系统时至关重要。模块化系统是一种无需重新编译即可更换其组件的系统。
    c.如果有许多跨领域的问题需要解决,尤其是在企业应用程序中,则至关重要。

对我来说,IoC/DI正在推出对调用对象的依赖关系。超级简单。

非技术性的答案是能够在你打开汽车之前更换发动机。如果一切都很好(界面),你就很好。

例如,任务#1是创建对象。如果没有IOC概念,任务#1应该由程序员完成。但是有了IOC概念,任务#1将由容器完成。

简而言之,控制从程序员反转到容器。因此,它被称为控制反转。

我找到了一个很好的例子这里

在使用控制反转之前,您应该清楚地知道它有其优点和缺点,并且如果您这样做,您应该知道为什么要使用它。

优点:

  • 您的代码被解耦,因此您可以轻松地将接口的实现与替代实现交换
  • 它是针对接口而不是实现编码的强大动力
  • 为您的代码编写单元测试非常容易,因为它只依赖于它在其构造函数/设置器中接受的对象,并且您可以轻松地使用隔离的正确对象初始化它们。

缺点:

  • IoC不仅反转了程序中的控制流,还使它变得非常模糊。这意味着您不能再只是阅读代码并从一个地方跳到另一个地方,因为通常存在于代码中的连接不再存在于代码中。相反,它在XML配置文件或注释中,以及在解释这些元数据的IoC容器的代码中。
  • 出现了一类新的错误,您的XML配置或注释错误,您可以花费大量时间找出为什么您的IoC容器在某些条件下将空引用注入您的一个对象。

就我个人而言,我看到了IoC的优点,我真的很喜欢它们,但我倾向于尽可能避免IoC,因为它将您的软件变成了一组不再构成“真实”程序的类的集合,而只是需要通过XML配置或注释元数据组合在一起的东西,没有它就会崩溃。

什么是控制反转?

如果你遵循这两个简单的步骤,你已经完成了控制反转:

  1. 什么待办事项部分与待办事项部分分开。
  2. 确保部分尽可能地了解什么部分;反之亦然。

根据您在实现中使用的技术/语言,这些步骤中的每一个都有几种可能的技术。

--

控制反转(IoC)的反转部分令人困惑;因为反转是相对术语。理解IoC的最好方法是忘记这个词!

--

示例

  • 事件处理。事件处理程序(待办事项部分)--引发事件(何时待办事项部分)
  • 依赖注入。构造依赖项(待办事项部分)的代码-在需要时为客户端实例化和注入该依赖项,这通常由DI工具(例如Dagger(当待办事项部分))处理。
  • 接口。组件客户端(何时待办部分)-组件接口实现(待办部分)
  • xUnit夹具。Setup和TearDown(待办事项部分)——xUnit框架在开头调用Setup,在结尾调用TearDown(待办事项部分)
  • 模板方法什么时候做部分--原始子类实现什么时候做部分
  • COM中的DLL容器方法。DllMain、DllCanUnload等(待办事项部分)——COM/OS(待办事项部分)

似乎关于“IoC”(首字母缩略词和它所代表的名称)最令人困惑的事情是它的名字太迷人了-几乎是一个噪音名称。

我们真的需要一个名字来描述过程驱动编程和事件驱动编程之间的区别吗?好吧,如果我们需要的话,但是我们需要选择一个全新的“比生命更重要”的名字吗?

假设我们在一家酒店开会。

我们邀请了很多人,所以我们遗漏了许多水壶和许多塑料杯。

当有人想喝的时候,他/她把杯子装满,喝水,然后把杯子扔在地板上。

大约一个小时后,我们的地板上覆盖着塑料杯和水。

让我们在反转控件后尝试一下:

想象一下在同一个地方举行同样的会议,但是我们现在有一个只有一个玻璃杯的服务员,而不是塑料杯(辛格尔顿)

当有人想喝酒时,服务员会为他们拿一杯。他们喝了它,然后把它还给服务员。

撇开卫生问题不谈,使用服务员(过程控制)更有效和经济。

这正是Spring(另一个IoC容器,例如:Guice)所做的。Spring IoC不是让应用程序使用new关键字创建它需要的东西(即拿一个塑料杯),而是为应用程序提供所需对象(一杯水)的相同杯子/实例(单例)。

把自己想象成这样一个会议的组织者:

示例:-

public class MeetingMember {
private GlassOfWater glassOfWater;
...
public void setGlassOfWater(GlassOfWater glassOfWater){this.glassOfWater = glassOfWater;}//your glassOfWater object initialized and ready to use...//spring IoC  called setGlassOfWater method itself in order to//offer to meetingMember glassOfWater instance
}

有用链接:-

一个简单的书面解释可以在这里找到

http://binstock.blogspot.in/2008/01/excellent-explanation-of-dependency.html

它说-

"任何重要的应用程序都由两个或多个类组成相互协作以执行一些业务逻辑。传统上,每个对象都负责获取自己的对与之协作的对象(其依赖项)的引用。应用DI时,对象在创建时被赋予它们的依赖关系时间由某个外部实体协调换句话说,依赖关系被注入到对象中。”

控制反转(Inversion of Control,或IoC)是关于获得自由(你结婚了,你失去了自由,你被控制了。你离婚了,你刚刚实施了控制反转。这就是我们所说的“解耦”。良好的计算机系统不鼓励一些非常亲密的关系。)更大的灵活性(你办公室的厨房只供应干净的自来水,这是你想喝的唯一选择。你的老板通过安装一台新咖啡机来实施控制反转。现在你可以灵活地选择自来水或咖啡。)和减少依赖(你的伴侣有工作,你没有工作,你在经济上依赖你的伴侣,所以你被控制了。你找到了一份工作,你已经实施了控制反转。良好的计算机系统鼓励依赖性。)

当你使用台式电脑时,你已经被奴役了(或者说,被控制了)。你必须坐在屏幕前看着它。用键盘打字,用鼠标导航。一个写得不好的软件会让你更加奴役。如果你用笔记本电脑代替台式电脑,那么你就有点颠倒了控制。你可以很容易地拿走它并四处走动。所以现在你可以用电脑控制你的位置,而不是你的电脑控制它。

通过实现控制反转,软件/对象消费者可以获得对软件/对象的更多控制/选项,而不是被控制或拥有更少的选项。

考虑到上述想法。我们仍然错过了IoC的一个关键部分。在IoC的场景中,软件/对象消费者是一个复杂的框架。这意味着你创建的代码不是由你自己调用的。现在让我们解释为什么这种方式更适合Web应用程序。

假设你的代码是一群工人。他们需要建造一辆汽车。这些工人需要一个地方和工具(软件框架)来建造汽车。一个传统的软件框架就像一个有许多工具的车库。所以工人需要自己制定计划,并使用这些工具来建造汽车。造车不是一件容易的事情,工人们很难正确地计划和合作。一个现代的软件框架就像一个现代汽车工厂,所有的设施和管理人员都到位。工人不需要制定任何计划,经理(框架的一部分,他们是最聪明的人,制定了最复杂的计划)将帮助协调,以便工人知道什么时候做他们的工作(框架调用你的代码)。工人只需要足够灵活地使用经理给他们的任何工具(通过使用依赖注入)。

虽然工人把管理项目的控制权交给了顶层的经理(框架)。但有一些专业人士帮忙是很好的。这是IoC的概念真正的来源。

具有MVC架构的现代Web应用程序依赖于框架来执行URL路由并将控制器放在框架调用的位置。

依赖注入和控制反转是相关的。依赖注入处于微型级别,控制反转处于级别。你必须吃每一口(实现DI)才能完成一顿饭(实现IoC)。

假设你是一个物体,你去一家餐馆:

没有ioc:你要求“苹果”,当你要求更多时,你总是得到苹果。

与ioc:你可以要求“水果”。每次你都可以得到不同的水果。例如,苹果、橙子或西瓜。

所以,很明显,当你喜欢这些品种时,IoC是首选。

控制反转是关于分离关注点。

没有ioc:你有一台排名第一的电脑,你不小心打破了屏幕。该死,你发现同样型号的笔记本电脑屏幕在市场上无处可寻。所以你卡住了。

与ioc:你有一台排名第一的电脑,你不小心打破了屏幕。你发现你可以从市场上抓取几乎任何台式机显示器,它与你的台式机很好地配合。

在这种情况下,您的台式机成功实现了IoC。它接受各种类型的显示器,而笔记本电脑则不接受,它需要一个特定的屏幕来固定。

IoC是关于反转你的代码和第三方代码(库/框架)之间的关系:

  • 在正常的s/w开发中,您编写main()方法并调用“库”方法。在控制中:)
  • 在IoC中,“框架”控制main()并调用您的方法。框架在控制中:(

依赖注入是关于控制在应用程序中的流动。传统的桌面应用程序的控制流从应用程序(main()方法)到其他库方法调用,但是使用DI控制流是颠倒的,框架负责启动你的应用程序,初始化它并在需要时调用你的方法。

最后你总是赢:)

我将写下我对这两个术语的简单理解:

For quick understanding just read examples*

依赖注入(DI):
依赖注入通常意味着将方法依赖的对象作为参数传递给方法,而不是让方法创建依赖对象
它在实践中的意思是该方法不直接依赖于特定的实现;任何满足要求的实现都可以作为参数传递。

用这个对象告诉他们的依赖关系。
这导致了松散耦合的应用程序开发。

Quick Example:EMPLOYEE OBJECT WHEN CREATED,IT WILL AUTOMATICALLY CREATE ADDRESS OBJECT(if address is defines as dependency by Employee object)

控制反转(IoC)容器:
这是框架的共同特征,IOC管理java对象
-通过其BeanFactory从实例化到销毁。
-由IoC容器实例化的Java组件称为bean,以及它已配置和编码的IoC容器管理bean的范围、生命周期事件和任何AOP功能

#0.

通过实现控制反转,软件/对象消费者可以获得对软件/对象的更多控制/选项,而不是被控制或拥有更少的选项。

控制反转作为设计指南用于以下目的:

某个任务的执行与实现脱钩。
每个模块都可以专注于它的设计目的。
模块不假设其他系统做什么,而是依赖他们的合同。
替换模块对其他模块没有副作用
我将在这里保持抽象,您可以访问以下链接以详细了解该主题。
一个很好的例子阅读

详细说明

编程演讲

简单地说,IoC:它使用接口作为特定事物(例如字段或参数)的一种方式,作为一些类可以使用的通配符。它允许代码的可重用性。

例如,假设我们有两个类:Cat。两者共享相同的质量/状态:年龄、大小、重量。因此,我可以创建一个名为狗服务CatService的服务类,而不是创建一个名为动物服务的服务类,它允许仅在使用接口I动物时使用Dog和Cat。

然而,从实用的角度来看,它有一些倒退。

a)大多数开发人员不知道如何使用它。例如,我可以创建一个名为客户我可以自动创建的类(使用IDE的工具)和一个名为客户的接口。所以,无论接口是否会被重用,找到一个装满类和接口的文件夹并不罕见。这被称为BLOATED。有些人可能会争辩说“也许将来我们可以使用它”。:-|

b)它有一些限制。例如,我们谈谈Cat的情况,我想添加一个只针对狗的服务(功能)。假设我想计算我需要训练一只狗的天数(#0),对于猫来说这是无用的,猫是无法训练的(我在开玩笑)。

b.1)如果我将trainDays()添加到服务动物服务,那么它也适用于猫,它根本无效。

b.2)我可以在trainDays()中添加一个条件,它评估使用哪个类。但它会完全破坏IoC。

B.3)我可以为新功能创建一个名为狗服务的新服务类。但是,它会增加代码的可运维性,因为我们将为创建两类服务(具有类似的功能),这很糟糕。

在类中创建对象称为紧耦合,Spring通过遵循设计模式(DI/IOC)来消除这种依赖。其中类的对象在构造函数中传递而不是在类中创建。此外,我们在构造函数中给出超类引用变量来定义更通用的结构。

使用IoC,您不是在更新您的对象。您的IoC容器将做到这一点并管理它们的生命周期。

它解决了必须手动将一种类型的对象的每个实例化更改为另一种类型的问题。

如果您的功能将来可能会发生变化,或者根据使用的环境或配置可能会有所不同,则适用。

控制反转是一个通用的原则,而依赖注入将此原则实现为对象图构造的设计模式(即配置控制对象如何相互引用,而不是对象本身控制如何获得对另一个对象的引用)。

将控制反转视为一种设计模式,我们需要看看我们正在反转的是什么。依赖注入反转构造对象图的控制。如果通俗地说,控制反转意味着程序中控制流的变化。例如。在传统的独立应用程序中,我们有main方法,从那里控制被传递给其他第三方库(在这种情况下,我们使用了第三方库的函数),但是通过控制反转,控制从第三方库代码转移到我们的代码中,因为我们正在使用第三方库的服务。但是还有其他方面需要在程序中反转-例如调用方法和线程来执行代码。

对于那些对控制反转感兴趣的人,已经发表了一篇论文,概述了控制反转作为一种设计模式的更完整的图景(Office Floor:使用办公模式来改进软件设计http://doi.acm.org/10.1145/2739011.2739013,免费副本可从http://www.officefloor.net/about.html下载)。

确定的是以下关系:

控制反转(对于方法)=依赖(状态)注入+连续注入+线程注入

可用的控制反转的上述关系摘要-http://dzone.com/articles/inversion-of-coupling-control

只回答第一部分。怎么了?

控制反转(IoC)意味着首先创建依赖项的实例和类的后一个实例(可选地通过构造函数注入它们),而不是先创建类的实例,然后类实例创建依赖项的实例。因此,控制反转反转程序的控制流而不是被叫控制控制流(在创建依赖项时),调用者控制程序的控制流程

为了理解这个概念,控制反转(IoC)或依赖反转原则(DIP)涉及两个活动:抽象和反转。依赖注入(DI)只是为数不多的反转方法之一。

阅读更多关于这个你可以阅读我的博客这里

  1. 这是什么?

这是一种实践,你让实际行为来自边界之外(面向对象编程中的类)。边界实体只知道它的抽象(例如接口、抽象类、面向对象编程中的委托)。

  1. 它解决了什么问题?

在编程方面,IoC试图通过使其模块化、解耦其各个部分并使其可单元测试来解决单体代码。

  1. 什么时候合适,什么时候不合适?

它在大多数情况下都是合适的,除非你只是想要单体代码(例如非常简单的程序)

我喜欢这个解释:http://joelabrahamsson.com/inversion-of-control-an-introduction-with-examples-in-net/

它开始很简单,并显示了代码示例。

在此处输入图片描述

消费者X需要被消费的类Y来完成某事。这一切都很好,很自然,但X真的需要知道它使用Y吗?

X知道它使用的东西具有Y的行为、方法、属性等,而不知道谁实际实现了这些行为,这还不够吗?

通过提取X在Y中使用的行为的抽象定义,如下所示,并让消费者X使用该行为的实例而不是Y,它可以继续做它所做的事情,而不必知道关于Y的细节。

在此处输入图片描述

在上面的插图中,Y实现了I,X使用了I的一个实例。虽然X很可能仍然使用Y,但有趣的是X并不知道这一点。它只知道它使用了实现I的东西。

阅读文章以获取更多信息和福利描述,例如:

  • X不再依赖于Y
  • 更灵活,可在运行时决定实现
  • 代码单元隔离,测试更简单

我知道答案已经在这里给出了。但我仍然认为,关于控制权倒置的一些基本知识必须在这里详细讨论,以供将来的读者使用。

控制反转(IoC)建立在一个非常简单的原则上,称为好莱坞原则。它说,

别打电话给我们我们会打给你的

它的意思是,不要去好莱坞实现你的梦想,而是如果你有价值,那么好莱坞会找到你,让你的梦想成真。

现在当我们讨论IoC的原则时,我们常常忘记好莱坞。对于IoC,必须有三个元素,好莱坞,你和完成梦想的任务。

在我们的编程世界里,好莱坞代表一个通用框架(可能是你自己或其他人写的),代表你写的用户代码,的任务代表你想用代码完成的事情。现在你永远不会自己触发你的任务,不是在IoC中!相反,你设计的一切都是这样的,你的框架会为你触发你的任务。因此,你建立了一个可重用的框架,它可以让某人成为英雄,也可以让某人成为恶棍。但那个框架总是负责的,它知道什么时候选择某人,而某人只知道它想成为什么。

这里将给出一个现实生活中的例子。假设您想开发一个Web应用程序。因此,您创建了一个框架,该框架将处理Web应用程序应该处理的所有常见事情,例如处理超文本传输协议请求、创建应用程序菜单、提供页面、管理cookie、触发事件等。

然后,您在框架中留下一些钩子,您可以在其中放置进一步的代码来生成自定义菜单,页面,cookie或记录一些用户事件等在每个浏览器请求上,您的框架将运行并执行您的自定义代码(如果挂钩),然后将其提供回浏览器。

所以,这个想法非常简单。与其创建一个控制一切的用户应用程序,不如首先创建一个可重用的框架,它将控制一切,然后编写自定义代码并将其挂钩到框架以及时执行这些代码。

Laravel和EJB就是这种框架的例子。

参考:

https://martinfowler.com/bliki/InversionOfControl.html

https://en.wikipedia.org/wiki/Inversion_of_control

我发现了一个非常清晰的例子这里,它解释了“控件是如何反转的”。

经典代码(无依赖注入)

以下是不使用DI的代码的大致工作方式:

  • 应用程序需要Foo(例如控制器),因此:
  • 应用程序创建Foo
  • 应用程序调用Foo
    • Foo需要Bar(例如服务),所以:
    • Foo创建Bar
    • Foo呼叫Bar
      • Bar需要Bim(一个服务,一个存储库,…),所以:
      • 酒吧创造Bim
      • 酒吧是有作用的

使用依赖注入

以下是使用DI的代码大致工作方式:

  • 应用程序需要Foo,它需要Bar,它需要Bim,所以:
  • 应用程序创建Bim
  • 应用程序创建Bar并赋予它Bim
  • 应用程序创建Foo并赋予它Bar
  • 应用程序调用Foo
    • Foo呼叫Bar
      • 酒吧是有作用的

依赖项的控制从一个被调用倒置到一个调用。

它解决了什么问题?

依赖注入使得与注入类的不同实现进行交换变得容易。在单元测试时,您可以注入虚拟实现,这使得测试变得容易得多。

例如:假设您的应用程序将用户上传的文件存储在Google Drive中,使用DI,您的控制器代码可能如下所示:

class SomeController{private $storage;
function __construct(StorageServiceInterface $storage){$this->storage = $storage;}
public function myFunction (){return $this->storage->getFile($fileName);}}
class GoogleDriveService implements StorageServiceInterface{public function authenticate($user) {}public function putFile($file) {}public function getFile($file) {}}

当您的需求发生变化时,例如,系统会要求您使用Dropbox而不是GoogleDrive。您只需为StorageServiceInterface编写一个Dropbox实现。只要Dropbox实现坚持StorageServiceInterface,您就不必对控制器进行任何更改。

在测试时,您可以使用虚拟实现为StorageServiceInterface创建模拟,其中所有方法都返回null(或根据您的测试要求返回任何预定义的值)。

相反,如果您有控制器类来使用new关键字构造存储对象,如下所示:

class SomeController{private $storage;
function __construct(){$this->storage = new GoogleDriveService();}
public function myFunction (){return $this->storage->getFile($fileName);}}

当您想使用Dropbox实现进行更改时,您必须替换构造new GoogleDriveService对象的所有行并使用DropboxService。此外,在测试某些控制器类时,构造函数总是期望GoogleDriveService类,并触发该类的实际方法。

什么时候合适,什么时候不合适?在我看来,当你认为有(或可能有)一个类的替代实现时,你会使用DI。

控制倒置是当你去杂货店时,你的妻子给你要购买的产品清单。

在编程术语中,她将回调函数getProductList()传递给您正在执行的函数-doShopping()

它允许函数的用户定义它的某些部分,使其更加灵活。

控制反转是将控制从库转移到客户端。当我们谈论一个客户端将函数值(lambda表达式)注入(传递)到控制(改变)库函数行为的高阶函数(库函数)时,它更有意义。

因此,这种模式的一个简单实现(具有巨大的含义)是一个高阶库函数(它接受另一个函数作为参数)。库函数通过赋予客户端提供“控制”函数作为参数的能力来转移对其行为的控制。

例如,像“map”、“platMap”这样的库函数是IoC实现。

当然,有限的IoC版本是例如布尔函数参数。客户端可以通过切换布尔参数来控制库函数。

将库依赖项(携带行为)注入库的客户端或框架也可能被视为IoC

控制反转意味着你控制组件(类)的行为。为什么它被称为“反转”,因为在这种模式之前,类是硬连线的,并且对它们将做什么是明确的。

您导入一个有TextEditorSpellChecker类的库。现在很自然,这个SpellChecker只会检查英语的拼写。假设您希望TextEditor处理德语并能够进行拼写检查,您可以控制它。

使用IoC,这个控件是倒置的,即它给你,如何?库将实现这样的东西:

它将有一个TextEditor类,然后它将有一个ISpeallChecker(这是一个接口而不是一个具体的SpellChecker类),当你在IoC容器中配置东西时,例如Spring,你可以提供你自己的“ISpellChecker”实现,它将检查德语的拼写。

这是什么?反转(耦合)控制,改变方法签名的耦合方向。使用反转控制,方法签名的定义由方法实现(而不是方法的调用者)决定。完整的解释在这里

它解决了什么问题?方法上的自顶向下耦合。这随后消除了重构的需要。

什么时候适合使用,什么时候不适合?对于小的定义良好的应用程序,它们不会受到太大的变化,这可能是一个开销。然而,对于将要发展的定义较少的应用程序,它减少了方法签名的固有耦合。这给了开发人员更多的自由来发展应用程序,避免了对代码进行昂贵的重构。基本上,允许应用程序在很少返工的情况下发展。

要了解IoC,我们应该谈谈依赖倒置。

依赖倒置:依赖于抽象,而不是具体。

控制反转:Main vs Abstraction,以及Main如何成为系统的粘合剂。

DIP and IoC

我写了一些很好的例子,你可以在这里查看:

https://coderstower.com/2019/03/26/dependency-inversion-why-you-shouldnt-avoid-it/

https://coderstower.com/2019/04/02/main-and-abstraction-the-decoupled-peers/

https://coderstower.com/2019/04/09/inversion-of-control-putting-all-together/

真的不明白为什么会有很多错误的答案,甚至被接受的答案也不太准确,让人很难理解。真相总是简单明了的。

作为@马克·哈里森的回答中的@Schneider评论,请阅读Martin Fowler讨论IoC的帖子。

https://martinfowler.com/bliki/InversionOfControl.html

我最喜欢的一个是:

这种现象就是控制反转(也被称为好莱坞原则-“不要打电话给我们,我们会打电话给你”)。

为啥?

Wiki for IoC,我可能会引用一个片段。

控制反转用于增加程序的模块化并使其可扩展……然后在2004年由罗伯特·马丁martinfowler进一步推广。

Robert C. Martin:<<Clean Code: A Handbook of Agile Software Craftsmanship>>作者。

Martin Fowler:<<Refactoring: Improving the Design of Existing Code>>的作者。

我已经读了很多关于这个问题的答案,但是如果有人仍然感到困惑,需要一个额外的“外行术语”来解释IoC,这是我的看法:

想象一下父母和孩子互相交谈。

没有IoC:

*家长:只有当我问你问题时,你才能说话,只有当我给你许可时,你才能行动。

家长:这意味着,如果我不问你,你不能问我是否可以吃饭、玩耍、上厕所甚至睡觉。

#30340;,你想吃?

儿童:没有。

好的,我会回来的。等等我。

儿童:(想玩,但由于父母没有问题,孩子什么也做不了)。

1小时后…

我回来了,你想玩吗?

儿童:是的。

家长:授予权限。

儿童:(终于可以玩了)。

这个简单的场景解释了控制以父母为中心。孩子的自由受到限制,高度依赖于父母的问题。当被要求说话时,孩子可以只有说话,当被授予许可时,孩子可以只有行动。

使用IoC:

孩子现在有能力提问,父母可以回答和权限。简单地说,控制是倒置的!孩子现在可以随时自由提问,尽管在权限方面仍然依赖于父母,但他在说话/提问的方式上并不依赖。

从技术上解释,这与控制台/shell/cmd与GUI交互非常相似。(这是马克·哈里森在2号顶部答案上的回答)。在控制台中,您依赖于向您询问/显示的内容,并且您不能跳转到其他菜单和功能而不首先回答它的问题;遵循严格的顺序流程。(从编程上讲,这就像一个方法/函数循环)。然而,使用GUI,菜单和功能被布置出来,用户可以选择它需要的任何东西,从而拥有更多的控制和更少的限制。(以编程方式,菜单在选择时具有回调,并且发生操作)。

由于这个问题已经有了很多答案,但没有一个显示了反转控制项的分解,我认为有机会给出一个更简洁和有用的答案。

控制反转是一种实现依赖反转原则的模式。依赖反转原则声明如下:1.高级模块不应该依赖于低级模块。两者都应该依赖于抽象(比如接口)。2.抽象不应该依赖于细节。细节(具体的实现)应该依赖于抽象。

有三种类型的控制反转:

接口反转提供者不应该定义接口。相反,消费者应该定义接口,提供者必须实现它。Interface Inversion允许消除每次添加新提供者时修改消费者的必要性。

流反转更改对流的控制。例如,您有一个控制台应用程序,您要求在其中输入许多参数,并且在每次输入参数后,您都必须按回车键。您可以在此处应用Flow Inversion并实现一个桌面应用程序,用户可以在其中选择参数输入的顺序,用户可以编辑参数,并且在最后一步,用户只需按一次回车键。

创建反转它可以通过以下模式实现:工厂模式、服务定位器和依赖注入。创建反转有助于消除类型之间的依赖关系,将依赖对象的创建过程移动到使用这些依赖对象的类型之外。为什么依赖关系不好?

控制倒置是程序中责任转移的指标。

每次授予依赖项直接作用于调用者空间的能力时,都会发生控制反转。

最小的IoC通过引用传递变量,让我们先看看非IoC代码:

function isVarHello($var) {return ($var === "Hello");}
// Responsibility is within the caller$word = "Hello";if (isVarHello($word)) {$word = "World";}

现在让我们通过将结果的责任从调用者转移到依赖项来反转控制:

function changeHelloToWorld(&$var) {// Responsibility has been shifted to the dependencyif ($var === "Hello") {$var = "World";}}
$word = "Hello";changeHelloToWorld($word);

下面是另一个使用OOP的例子:

<?php
class Human {private $hp = 0.5;
function consume(Eatable $chunk) {// $this->chew($chunk);$chunk->unfoldEffectOn($this);}
function incrementHealth() {$this->hp++;}function isHealthy() {}function getHungry() {}// ...}
interface Eatable {public function unfoldEffectOn($body);}
class Medicine implements Eatable {function unfoldEffectOn($human) {// The dependency is now in charge of the human.$human->incrementHealth();$this->depleted = true;}}
$human = new Human();$medicine = new Medicine();if (!$human->isHealthy()) {$human->consume($medicine);}
var_dump($medicine);var_dump($human);

*)免责声明:现实世界中的人类使用消息队列。

IoC原则有助于设计松散耦合的类,使它们可测试、可维护和可扩展。

我在使用库或框架的上下文中考虑控制反转。

传统的“控制”方式是我们构建一个控制器类(通常是main,但它可以是任何东西),导入一个库,然后使用您的控制器类来“控制”软件组件的操作。就像你的第一个C/Python程序(在Hello World之后)。

import pandas as pddf = new DataFrame()# Now do things with the dataframe.

在这种情况下,我们需要知道DataFrame是什么才能使用它。你需要知道使用什么方法,它需要什么值等等。如果你通过多态将其添加到自己的类中或只是重新调用它,你的类将需要DataFrame库才能正常工作。

“控制反转”意味着过程被颠倒。你的类不再控制库、框架或引擎的元素,而是注册类并将它们发送回被控制的引擎。换句话说,IoC可以意味着我们正在使用我们的代码来配置一个框架。你也可以将其视为类似于我们使用mapfilter中的函数来处理列表中数据的方式,只是将其应用于整个应用程序。

如果你是构建引擎的人,那么你可能正在使用依赖注入方法(如上所述)来实现这一目标。如果你是使用引擎的人(更常见),那么你应该能够只声明类,添加适当的符号,让框架为你完成其余的工作(例如创建路由、分配servlet、设置事件、输出小部件等)。

我觉得用这么多先前的答案回答这个问题有点尴尬,但我只是不认为任何答案都足够简单地说明了这个概念。

所以我们开始…

在非IOC应用程序中,您将编写流程并在其中包含所有详细步骤。考虑一个创建报告的程序-它将包含设置打印机连接、打印标题、遍历详细记录、然后打印页脚、可能执行换页等的代码。

在IOC版本的报表程序中,您将配置一个通用的、可重用的Report类的实例——也就是说,一个包含打印报表的过程流但其中没有任何详细信息的类。您提供的配置可能使用DI来指定Report应该调用哪个类来打印页眉,Report应该调用哪个类来打印详细行,以及Report应该调用哪个类来打印页脚。

因此,控制的反转来自控制过程,而不是您的代码,而是包含在一个外部的、可重用的类(Report)中,该类允许您指定或注入(通过DI)报告的细节-页眉、细节行、页脚。

您可以使用相同的Report类(控制类)生成任意数量的不同报告-通过提供不同的详细类集。您通过依赖Report类提供它来反转控件,并仅通过注入指定报告之间的差异。

在某些方面,IOC可以与驱动器备份应用程序进行比较-备份始终执行相同的步骤,但备份的文件集可能完全不同。

现在来具体回答最初的问题…

  • 它是什么?IOC依赖于一个可重用的控制器类,并提供特定于您手头问题的细节。
  • 它解决了哪个问题?避免您必须重述控制过程流。
  • 什么时候适合使用,什么时候不适合?每当您创建控制流始终相同且仅更改细节的过程流时。在创建一次性自定义过程流时,您不会使用它。

最后,IOC不是DI,DI也不是IOC——DI通常可以用在IOC中(为了说明抽象控制类的细节)。

不管怎样,我希望这有帮助。