什么是控制倒置?

Mik*_*llo 1704 oop design-patterns inversion-of-control

控制反转(或IoC)在第一次遇到时会非常混乱.

  1. 它是什么?
  2. 它解决了哪个问题?
  3. 何时适合使用,何时不适用?

uri*_*ini 1404

控制反转(IoC)和依赖注入(DI)模式都是关于从代码中删除依赖关系.

例如,假设您的应用程序具有文本编辑器组件,并且您希望提供拼写检查.您的标准代码如下所示:

public class TextEditor {

    private SpellChecker checker;

    public TextEditor() {
        this.checker = new SpellChecker();
    }
}
Run Code Online (Sandbox Code Playgroud)

我们在这里所做的创建之间的相关性TextEditorSpellChecker.在IoC场景中,我们会做这样的事情:

public class TextEditor {

    private IocSpellChecker checker;

    public TextEditor(IocSpellChecker checker) {
        this.checker = checker;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

SpellChecker sc = new SpellChecker; // dependency
TextEditor textEditor = new TextEditor(sc);
Run Code Online (Sandbox Code Playgroud)

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

这只是一个简单的例子,Simone Busoli 有一系列很好的文章更详细地解释了它.

  • 尽管有很多选票,但这个答案是不正确的.请参阅http://martinfowler.com/articles/injection.html#InversionOfControl.特别要注意的是,"控制反转过于通用,因此人们会觉得它很混乱.因此,与各种IoC倡导者进行了大量讨论后,我们选择了依赖注入的名称". (242认同)
  • 好清楚的例子.但是,假设不是要求将ISpellChecker接口传递给对象的构造函数,而是将其作为可设置属性(或SetSpellChecker方法)公开.这还会构成IoC吗? (47认同)
  • 我同意@Rogeria.这并没有解释为什么它被称为IoC而且我对投票数量感到惊讶;-) (39认同)
  • 我支持@Rogerio和@Pangea.这可能是[构造函数注入](http://www.martinfowler.com/articles/injection.html#ConstructorInjectionWithPicocontainer)的一个很好的例子,但对原始问题不是一个好的答案.如[Fowler](http://martinfowler.com/bliki/InversionOfControl.html)所定义的IoC可以在不使用任何类型的注入的情况下实现,例如通过使用[服务定位器](http://www.martinfowler) .com/articles/injection.html#UsingAServiceLocator)甚至是简单的继承. (26认同)
  • chainguy1337 - 是的.使用这样的setter称为setter注入,而不是构造函数注入(两种依赖注入技术).IoC是一种相当通用的模式,但依赖注入可以实现IoC (25认同)
  • 您给出的示例不是IOC,而是依赖注入示例. (14认同)
  • 这只是 DI,并没有清楚地解释 IOC,第二个和第三个答案怎么样? (3认同)
  • 所以,这个答案只是展示了实现 IoC 的“方法”,而不是解释这个概念,对吗? (2认同)
  • 原始依赖图看起来像(main - > TextEditor - > SpellChecker),但在第二个代码片段中,它看起来像(main - > TextEditor,main - > SpellChecker).从TextEditor到SpellChecker的依赖已被破坏,这很有用,所以我可以看到这是依赖注入的一个例子.但是,控制线程仍然以main开头,它调用texteditor中的代码.所以我不清楚为什么这会是"控制反转"的例子. (2认同)
  • ISpellChecker是一个接口,而不是已定义的类.因此,任何实现该接口的类都可以"注入"TextEditor.创建TextEditor的人可以确定它使用的ISpellChecker的实现.这个答案描述了依赖注入,与IoC无关. (2认同)
  • @Rogeiro,据我所知,"IoC"只是一种用于从应用程序中删除依赖关系的通用模式.如果answare仅提供一种称为"依赖注入模式"的解决方案,但还有其他可能以不同方式实现类似结果,例如服务定位器.对我而言,诚实的只是"依赖注入"工厂的包装......? (2认同)
  • 我建议作者更新答案,因为人们显然对依赖注入和控制反转的关系感到困惑,我非常喜欢这个简单且无框架的示例。我建议也给出一个服务定位器的例子。 (2认同)
  • 这不是 IOC 的例子,而是 DI 的例子,注意下面的例子 (2认同)
  • 您的 IoC 示例是依赖注入,显示的代码与控制反转无关。 (2认同)
  • 我真的很惊讶这个错误的答案怎么会得到这样的支持 (2认同)

Mar*_*son 593

控制反转是程序回调时得到的,例如gui程序.

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

print "enter your name"
read name
print "enter your address"
read address
etc...
store in database
Run Code Online (Sandbox Code Playgroud)

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

在GUI程序或某些程序中,我们说:

when the user types in field a, store it in NAME
when the user types in field b, store it in ADDRESS
when the user clicks the save button, call StoreInDatabase
Run Code Online (Sandbox Code Playgroud)

因此,现在控制被反转...用户不是以固定顺序接受用户输入,而是控制输入数据的顺序,以及何时将数据保存在数据库中.

基本上,任何具有事件循环,回调或执行触发器的东西都属于这一类.

  • 不要标记这个家伙.从技术上来说,他是正确的http://martinfowler.com/bliki/InversionOfControl.html IoC是一个非常普遍的校长.控制流由依赖注入"反转",因为您已经有效地将依赖性委托给某个外部系统(例如IoC容器) (148认同)
  • 实际上,这是为数不多的*正确的*anwsers之一!大家好,IoC从根本上讲不是依赖关系.一点也不. (37认同)
  • 同意Schneider的评论.5 downvotes?心灵难以置信,因为这是唯一真正正确的答案.请注意开头:'**喜欢**一个gui程序.依赖注入只是IoC最常见的实现. (35认同)
  • +1 - 这是Martin Fowler对以下声明的一个很好的描述(带有示例) - "早期用户界面由应用程序控制.您将有一系列命令,如"输入名称","输入地址";您的程序将驱动提示并为每个提取响应.使用图形(甚至基于屏幕)的UI,UI框架将包含此主循环,而您的程序则为屏幕上的各个字段提供事件处理程序.程序被颠倒了,离开了你的框架." (12认同)
  • 我现在明白为什么它有时被称为"好莱坞原则:不要打电话给我们,我们会打电话给你" (12认同)
  • 只是出色的答案. (4认同)
  • 真棒.我喜欢这个答案.它是一种纯粹的理解(而不是谈论代码而不是理解) (2认同)
  • 我发现这最好地回答了这个问题,因为 10 个其他答案中有 9 个只是将 IoC 与抽象联系起来。非真正谈论控制如何反转。+1 (2认同)

rpa*_*abi 405

什么是控制倒置?

如果你按照这两个简单的步骤,你就完成了控制反转:

  1. 什么做的部分与何时做的部分分开.
  2. 确保部分知道的地了解哪些部分; 反之亦然.

根据您实施的技术/语言,每种步骤都有几种可能的技术.

-

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

-

例子

  • 事件处理.事件处理程序(要做什么的部分) - 提升事件(什么时候做部分)
  • 接口.组件客户端(部分时间) - 组件接口实现(可执行部分)
  • xUnit fixure.Setup和TearDown(有待完成的部分) - xUnit框架在开始时调用Setup,在结束时调用TearDown(when-to-do部分)
  • 模板方法设计模式.模板方法什么时候做的部分 - 原始子类实现什么做的部分
  • COM中的DLL容器方法.DllMain,DllCanUnload等(要做什么的部分) - COM/OS(什么时候做部分)

  • 你怎么说接口。当我们使用接口时,组件客户端(何时执行部分)作为“何时”没有意义(例如:依赖注入),我们只是将其抽象出来并给予客户端添加任何实现的灵活性,但没有“何时”涉及那里。在引发事件处理事件的情况下,我同意“何时”。 (2认同)
  • @rpattabi,给出一个你在答案中所说的代码示例 (2认同)

Luo*_*Hui 148

控制反转是关于分离问题.

没有IoC:你有一台笔记本电脑,你不小心打破了屏幕.而且,你会发现同样型号的笔记本电脑屏幕在市场上无处可去.所以你被卡住了.

使用IoC:你有一台台式电脑,你不小心打破了屏幕.您发现几乎可以从市场上购买任何桌面显示器,它可以很好地与您的桌面配合使用.

在这种情况下,您的桌面成功实现了IoC.它接受各种类型的显示器,而笔记本电脑没有,它需要一个特定的屏幕来修复.

  • 错误答案.您正在解释_dependency injection_而不是IoC.请参阅Rogério对[上面这个答案]的评论(/sf/answers/219831/) (4认同)
  • Jijong Hui这个清除了我的脑袋. (3认同)
  • 大多数设计模式(如果不是全部的话)在我们的日常生活中都有我们看到和理解的对应物.理解设计模式的最有效方式是了解他们的日常生活.我相信有很多. (3认同)
  • 我同意。这是 DI,不是 IoC。仍然得到了赞成,因为它是一种简单的方法,但有助于扩展对该主题的理解。 (2认同)
  • 它解释了依赖注入。不是Ioc。但很好和清晰的解释。 (2认同)

Luo*_*Hui 100

控制反转(或IoC)是关于获得自由(你结婚,你失去了自由,你被控制.你离婚了,你刚刚实施了控制反转.这就是我们所说的,"解耦".良好的计算机系统劝阻一些非常亲密的关系.)更灵活(办公室的厨房只提供干净的自来水,这是你想要喝的唯一选择.你的老板通过设置一台新的咖啡机实现了控制倒置.现在你得到了选择自来水或咖啡的灵活性.)和较少的依赖(你的伴侣有工作,你没有工作,你在经济上依赖你的伴侣,所以你受到控制.你找到一份工作,你已经实施了倒置控制.良好的计算机系统鼓励依赖.)

当您使用台式计算机时,您已经从事(或说,控制).你必须坐在屏幕前看看它.使用键盘键入并使用鼠标进行导航.一个编写得很糟糕的软件可以让你更加依赖.如果用笔记本电脑替换桌面,那么你有点颠倒控制.你可以轻松地拿走它并四处走动.因此,现在您可以控制计算机的位置,而不是控制计算机的计算机.

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

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

假设您的代码是一组工作者.他们需要建造一辆汽车.这些工人需要一个地方和工具(软件框架)来制造汽车.一个传统的软件框架将像了很多工具,一个车库.因此,工人需要自己制定计划并使用工具来制造汽车.建造汽车并不是一件容易的事,工人们很难正确地规划和合作.一个现代的软件框架将是像现代汽车厂与地方所有的设施和管理人员.工作人员不必制定任何计划,管理人员(框架的一部分,他们是最聪明的人并制定最复杂的计划)将帮助协调,以便工人知道何时完成工作(框架调用您的代码).工人只需要足够灵活,可以使用经理给他们的任何工具(通过使用依赖注入).

虽然工人将管理项目的控制权交给管理者(框架).但是让一些专业人士帮忙是件好事.这就是IoC真正来自的概念.

具有MVC体系结构的现代Web应用程序依赖于进行URL路由的框架,并将控制器放置到适合框架调用的位置.

依赖注入和控制反转是相关的.依赖注入处于微观层面,控制反转处于宏观层面.你必须吃每一口(实施DI)才能吃完饭(实施IoC).

  • 我投票支持比较DI婚姻和IoC离婚. (16认同)
  • 给已婚人士的信息:现在不要离婚,您的孩子班级也可能实施 IoC。 (6认同)

ahe*_*ahe 89

在使用Inversion of Control之前,你应该清楚它有它的优点和缺点,你应该知道为什么要使用它.

优点:

  • 您的代码被解耦,因此您可以轻松地与其他实现交换接口的实现
  • 它是针对接口而不是实现进行编码的强大动力
  • 为代码编写单元测试非常容易,因为它只取决于它在构造函数/ setter中接受的对象,你可以轻松地使用正确的对象初始化它们.

缺点:

  • IoC不仅可以反转程序中的控制流程,还可以显着地覆盖它.这意味着您不能再只读取代码并从一个地方跳转到另一个地方,因为代码中通常存在的连接不再存在于代码中.相反,它在XML配置文件或注释中以及IoC容器的代码中解释这些元数据.
  • 出现了一类新的错误,你的XML配置或注释错误,你可以花很多时间找出你的IoC容器在某些条件下为你的一个对象注入空引用的原因.

就个人而言,我看到了IoC的优点,我真的很喜欢它们,但我倾向于尽可能地避免使用IoC,因为它会将您的软件变成一个不再构成"真正"程序的类集合,而只需将它们放在一起XML配置或注释元数据会在没有它的情况下掉落(和掉落).

  • 我认为他的意思是,你不能只读:myService.DoSomething()并转到DoSomething的定义,因为对于IoC,myService只是一个接口,你不知道实际的实现,除非你去看看它在xml配置文件或你的ioc设置的主要方法. (20认同)
  • Re:_it将你的软件变成一个不再构成"真正的"程序的类的集合,但只需要通过XML配置或注释元数据组合在一起,并且在没有它的情况下会掉落(并且掉落) - 我认为这是非常误导.任何编写在框架之上的程序都可以这样说.与良好的IoC容器的区别在于,如果您的程序设计和编写得很好,您应该能够将其取出并在代码中进行最少的更改,或者完全丢弃IoC并手动构建您的对象. (9认同)
  • 这就是Resharper帮助的地方 - 针对界面"点击进入实施".避免使用IoC(或者更具体地说,来自您的示例中的DI)可能也意味着您没有正确测试 (7认同)
  • 很高兴看到像这样的现实世界的答案!我认为有很多经验丰富的程序员,对于面向对象的设计和TDD实践感到满意,在这个"IoC"流行语被发明之前,已经使用了接口,工厂模式,事件驱动模型和模拟它们有意义的地方.不幸的是,如果你不使用他们喜欢的框架,太多的开发人员/"架构师"会声称做法不好.我更喜欢更好的设计,使用内置的语言概念和工具,以复杂性的一小部分实现相同的目标,即没有像你说的那样"蒙上阴影":-) (7认同)
  • 第一个骗局不正确.理想情况下,代码中只应使用1个IOC容器,这是您的主要方法.其他一切都应该从那里开始 (3认同)
  • 您可以在没有接口,注释或任何框架支持的情况下执行依赖项注入.至少,您需要的只是具有构造函数的类,这些构造函数可以获取所需的其他对象的实例.最大的区别是你不允许这些类自己创建东西.一个简单的main方法可以扮演依赖注入框架的角色,只需在每个对象上调用new.写这个主要方法很乏味.但是其余代码仍然可读且干净,并且在一个地方安装管道实际上也很不错. (3认同)

Nil*_*ect 64

  1. 维基百科文章.对我而言,控制反转正在转变您按顺序编写的代码并将其转换为委托结构.您的程序不是使用程序显式控制所有内容,而是设置一个具有某些功能的类或库,以便在发生某些事情时调用它们.

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

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

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

  • 我发现维基百科文章非常混乱,需要修复.查看讨论页面,了解笑声. (35认同)
  • 编写自己的事件循环仍然可能是控制反转,如果该事件循环取代了框架,并且其余代码使用您刚刚编写的框架的 IoC 原理。这实际上并不是一件坏事,它增加了可读性,但代价只是多了一点编码(并不是说它总是合适的)。 (2认同)

Mic*_*der 43

但我认为你必须非常小心.如果您过度使用此模式,您将进行非常复杂的设计和更复杂的代码.

就像在TextEditor的这个例子中一样:如果你只有一个SpellChecker可能没有必要使用IoC?除非您需要编写单元测试或其他东西......

无论如何:要合理.设计模式是良好的做法,但不是圣经可以传播.不要把它粘在任何地方.

  • 你怎么知道你只有一个拼写检查器? (2认同)

Luo*_*Hui 42

假设你是一个对象.你去餐馆吃饭:

没有IoC:你要求"苹果",当你提出更多问题时,你总会得到苹果.

使用IoC:你可以要求"水果".每次送达时,您都可以获得不同的水果.例如,苹果,橙子或西瓜.

因此,显然,当您喜欢这些品种时,IoC是首选.


fer*_*der 41

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

非技术性的答案是能够在开启之前更换汽车中的引擎.如果一切正常(界面),你很好.


Gle*_*ock 25

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

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

  3. 一个.在进行测试驱动开发时很关键.没有IoC,测试就很困难,因为被测组件与系统的其余部分高度耦合.
    湾 开发模块化系统时至关重要 模块化系统是一种系统,其组件可以在不需要重新编译的情况下进行更换.
    C.如果在企业应用程序中有许多需要解决的跨领域问题,则至关重要.

  • 实际上,IoC主要不是关于管理依赖关系.请参阅http://martinfowler.com/articles/injection.html#InversionOfControl特别要注意的部分是"控制反转过于通用一个术语,因此人们会觉得它很混乱.因此需要进行大量讨论各种IoC倡导者我们选择了Dependency Injection这个名称". (2认同)

use*_*678 23

仅回答第一部分.它是什么?

控制反转(IoC)意味着创建依赖关系的第一个和后一个实例的实例(可选择通过构造函数注入它们),而不是先创建类的实例,然后创建依赖关系实例的类实例.因此,控制的反转反转控制的流程的程序.代替被叫方控制控制的流程(在创建依赖关系),则主叫方控制程序的控制的流程.

  • 在我 15 年的编程生涯中,这是迄今为止对 IoC 的最清晰的解释。谢谢!主要答案*没有说*它是关于类或对象的创建,恕我直言。它以创建类为例说明了控制反转是什么。:) 作者使用最少的句子和单词来传达答案!恕我直言,这个答案是本文中最好的答案。为什么它是最后找到的条目之一...... (2认同)

Vde*_*deX 21

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

For quick understanding just read examples*
Run Code Online (Sandbox Code Playgroud)

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

用这个对象告诉他们的依赖关系.春天使它可用.
这导致松散耦合的应用程序开发.

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

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

QUICK EXAMPLE:Inversion of Control is about getting freedom, more flexibility, and less dependency. When you are using a desktop computer, you are slaved (or say, controlled). You have to sit before a screen and look at it. Using keyboard to type and using mouse to navigate. And a bad written software can slave you even more. If you replaced your desktop with a laptop, then you somewhat inverted control. You can easily take it and move around. So now you can control where you are with your computer, instead of computer controlling it.

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

作为设计指南的控制反转具有以下目的:

将某个任务的执行与实现分离开来.
每个模块都可以专注于它的设计目标.
模块不会假设其他系统的功能,而是依赖于合同.
更换模块对其他模块没有任何副作用
我将在这里保持抽象,您可以访问以下链接以了解该主题的详细信息.
一个很好的阅读与例子

详细解释


Pet*_*rns 17

我同意NilObject,但我想补充一点:

如果你发现自己复制了整个方法而只改变了一小段代码,你可以考虑用控制反转来解决它

如果你发现自己复制和粘贴周围的代码,你总是在做一些错误的.作为设计原则编纂一次且仅一次.


gog*_*ogs 17

例如,任务#1是创建对象.没有IOC概念,任务#1应该由Programmer完成.但是使用IOC概念,任务#1将由容器完成.

简而言之,Control从Programmer转换为容器.因此,它被称为控制反转.

我在这里找到了一个好例子.

  • 什么是容器? (3认同)

Jai*_*dra 17

我们说我们在一些酒店开会.

很多人,许多玻璃水瓶,许多塑料杯.

当有人想喝酒时,她会把杯子,饮料和杯子倒在地板上.

一小时后,我们的地板上覆盖着塑料杯和水.

让反转控制.

在同一个地方同一次会议,但我们有一个带一个玻璃杯的服务员(Singleton),而不是塑料杯

她总是向客人提供饮酒.

当有人想喝酒的时候,她会从服务员的杯子里拿出来喝,然后把它还给服务员.

撇开卫生问题,最后一种形式的饮用过程控制更加有效和经济.

这正是Spring(另一个IoC容器,例如:Guice)所做的.而不是让应用程序使用新关键字(取塑料杯)创建它所需要的东西,Spring IoC容器始终为应用程序提供所需对象(玻璃水)的相同实例(单例).

把自己想象成这种会议的组织者.您需要向酒店管理部门发送消息的方式

会议成员需要一杯水而不是一块蛋糕.

例:-

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

}
Run Code Online (Sandbox Code Playgroud)

有用的链接: -


小智 16

看起来关于"IoC"这个首字母缩写词和它所代表的名字最令人困惑的是,它的名字太过迷人 - 几乎是一个噪音名称.

我们真的需要一个名称来描述程序和事件驱动编程之间的区别吗?好吧,如果我们需要,但是我们是否需要选择一个全新的"比生命更重要"的名称,这个名称比它解决的更加困惑?

  • IoC!=事件驱动。相似之处(在某些情况下是重叠的),但它们基本上不是相同的范式。 (2认同)

mar*_*1ua 14

控制权的反转就是当你去杂货店时,你的妻子会给你一份要买的产品清单.

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

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

  • 我的妻子经常和我一起购物,但我同意这个说法. (5认同)
  • 如果她也给钱,则称为 DI。 (2认同)

aga*_*ase 12

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

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

它说 -

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


小智 12

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

将控制反转看作一种设计模式,我们需要看看我们正在反转的是什么.依赖注入反转了构造对象图的控制.如果以外行人的话说,控制权的反转意味着计划中控制流的变化.例如.在传统的独立的应用程序,我们的主要方法,从那里控制被传递给其他第三方库(在例子中,我们已经使用第三方库的功能),而是通过控制控制反转得到来自第三方库的代码转移到我们的代码,因为我们正在服务第三方图书馆.但是还有其他方面需要在程序中反转 - 例如调用方法和线程来执行代码.

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

确定的是以下关系:

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


小智 11

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

  • 在正常的s/w开发中,编写main()方法并调用"library"方法. 在掌控:)
  • 在IoC中,"框架"控制main()并调用您的方法.该框架处于控制之中:(

DI(依赖注入)是关于控件如何在应用程序中流动的.传统的桌面应用程序具有从应用程序(main()方法)到其他库方法调用的控制流,但是DI控制流被反转,框架负责启动应用程序,初始化它并在需要时调用您的方法.

最后你总是赢:)


Rag*_*a N 11

我在这里找到了一个非常明确的例子,它解释了"控制是如何反转的".

经典代码(没有依赖注入)

以下是不使用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云端硬盘中,使用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) {}
}
Run Code Online (Sandbox Code Playgroud)

当您的要求发生变化时,系统会要求您使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

当您想要使用Dropbox实现进行更改时,您必须替换new构建GoogleDriveService对象的所有行并使用DropboxService.此外,在测试SomeController类时,构造函数总是期望GoogleDriveService类和此类的实际方法被触发.

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


DDa*_*Dan 9

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

它从简单开始,并显示代码示例.

在此输入图像描述

消费者X需要消费类Y来完成某些事情.这一切都是好的和自然的,但X真的需要知道它使用Y吗?

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

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

在此输入图像描述

在上面的插图中,Y实现I和X使用I的实例.虽然X仍然很可能使用Y,但有趣的是X不知道.它只知道它使用的东西实现我.

阅读文章了解更多信息和福利描述,例如:

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

...


mag*_*nes 8

编程说话

IoC简单易用:它使用Interface作为特定事物(如字段或参数)的一种方式,作为可由某些类使用的通配符.它允许代码的可重用性.

例如,假设我们有两个类:DogCat.两者都具有相同的品质/状态:年龄,体型,体重.因此,而不是创建一个服务等级称为DogServiceCatService,我可以创建一个单一的一个叫AnimalService,只允许他们是否使用该接口使用狗和猫IAnimal.

然而,从实用角度讲,它有一些倒退.

a)大多数开发人员不知道如何使用它.例如,我可以创建一个名为Customer的类,我可以自动创建(使用IDE的工具)一个名为ICustomer的接口.因此,无论接口是否被重用,找到一个充满类和接口的文件夹并不罕见.它被称为BLOATED.有些人可能会争辩说"将来可能会使用它".: - |

b)它有一些限制.例如,让我们谈谈的情况,我想只为狗添加新的服务(功能).让我们说我想要计算我需要训练狗的天数(trainDays()),因为猫没用,猫不能训练(我在开玩笑).

b.1)如果我添加trainDays()到Service AnimalService,它也适用于猫,它根本无效.

b.2)我可以在trainDays()评估使用哪个类的位置添加条件.但它会彻底打破IoC.

b.3)我可以为新功能创建一个名为DogService的新服务类.但是,它会增加代码的可维护性,因为我们将为Dog提供两类服务(具有类似的功能)并且它很糟糕.


Abd*_*ooq 8

我知道这里已经给出了答案。但是我仍然认为,必须在这里详细讨论一些有关控制反转的基础,以供将来的读者使用。

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

不要打电话给我们,我们会打电话给你

这意味着不要去好莱坞实现自己的梦想,而是如果你值得,好莱坞就会找到你并使自己的梦想成真。倒过来了吧?

现在,当我们讨论IoC原理时,我们常常会忘记好莱坞。对于IoC而言,必须有三个要素,好莱坞,您和完成梦想的任务。

在我们的编程世界中,Hollywood表示一个通用框架(可以由您或其他人编写),表示编写的用户代码,而任务表示您要用代码完成的任务。现在,您永远都不会自己去触发任务,而不是在IoC中!而是设计了所有内容,使您的框架可以为您触发任务。因此,您建立了一个可重用的框架,该框架可以使某人成为英雄,而另一个人成为坏人。但是该框架始终负责,它知道何时选择某人,而某人只知道它想要成为什么样的人。

这里将给出一个真实的例子。假设您要开发一个Web应用程序。因此,您将创建一个框架,该框架将处理Web应用程序应处理的所有常见问题,例如处理http请求,创建应用程序菜单,提供页面,管理Cookie,触发事件等。

然后,在框架中留下一些钩子,您可以在其中放置更多代码以生成自定义菜单,页面,cookie或记录一些用户事件等。在每次浏览器请求时,框架都会运行并执行您的自定义代码(如果已钩住),然后将其放回浏览器。

因此,这个想法非常简单。首先,您要创建一个可重用的框架来控制所有内容,然后编写您的自定义代码并将其挂接到框架上,以及时执行这些代码,而不是创建将控制所有内容的用户应用程序。

Laravel和EJB是此类框架的示例。

参考:

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

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

  • 我在这里找到的最合适的答案。 (2认同)

Vad*_* S. 8

由于这个问题已经有很多答案,但没有一个显示反转控制术语的细分,我看到有机会提供更简洁和有用的答案。

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

控制反转分为三种:

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

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

创建反转 可以通过以下模式实现:工厂模式、服务定位器和依赖注入。创建反转有助于消除类型之间的依赖关系,将依赖对象创建过程移到使用这些依赖对象的类型之外。为什么依赖关系不好?这里有几个例子:在代码中直接创建一个新对象会使测试变得更加困难;在不重新编译的情况下无法更改程序集中的引用(违反 OCP 原则);你不能轻易地用网络用户界面替换桌面用户界面。


Reu*_*uta 7

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

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

没有IoC:

*家长:您只有在我问您问题时才能讲话,并且只有在我允许您的情况下才能采取行动。

父母:这意味着,如果我不问你,你就不能问我是否可以吃饭,娱乐,上厕所甚至睡觉。

父母:你想吃吗?

小孩:没有

父母:好的,我会回来的。等等我。

小孩:(想玩,但由于父母没有问题,所以小孩无能为力)。

1小时后...

父母:我回来了。你想玩吗?

孩子:是的。

父级:已授予权限。

孩子:(最终可以玩)。

这个简单的场景说明了控件以父控件为中心。孩子的自由受到限制,在很大程度上取决于父母的问题。孩子只有在被要求讲话时才可以讲话,并且只有在获得许可后才可以采取行动。

使用IoC:

现在,孩子可以提出问题了,父母可以回答并获得许可。仅仅意味着控件是反向的!现在,孩子可以随时随地提问,尽管与父母之间在权限方面仍然存在依赖性,但他并不依赖于说话/问问题的方式。

用技术上的解释,这与console / shell / cmd vs GUI交互非常相似。(哪个是马克·哈里森的答案高于第二名的最高答案)。在控制台中,您依赖于所要/显示的内容,并且必须先回答问题才能跳到其他菜单和功能。遵循严格的顺序流程。(以编程方式,这就像一个方法/功能循环)。但是,使用GUI可以布置菜单和功能,并且用户可以选择所需的任何内容,从而具有更多的控制权和更少的限制。(以编程方式,选择菜单时会回调,并且会执行操作)。


小智 6

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


Ser*_*iuc 5

控制反转是将控制权从库转移到客户端。当我们谈论一个将函数值(lambda表达式)注入(传递)到控制(更改)库函数行为的高阶函数(库函数)中的客户端时,这更有意义。将库依赖项(带有行为)注入库中的客户端或框架也可以视为IoC


Rod*_*ati 5

我觉得用这么多先前的答案来回答这个问题有点尴尬,但我只是认为没有一个答案足够简单地阐述了这个概念。

那么我们开始吧...

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

在报表程序的 IOC 版本中,您可以配置一个通用的、可重用的报表类的实例,即包含打印报表的处理流程的类,但其中不包含任何详细信息。您提供的配置可能使用 DI 来指定报表应调用哪个类来打印页眉、报表应调用哪个类来打印详细信息行以及报表应调用哪个类来打印页脚。

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

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

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

现在具体回答原来的问题......

  • 它是什么?IOC 依赖于可重用的控制器类,并提供针对您当前问题的详细信息。
  • 它解决哪个问题?使您无需重述控制流程。
  • 什么时候适合使用,什么时候不适合使用?每当您创建流程时,控制流程始终相同,仅更改细节。在创建一次性的自定义流程时,您不会使用它。

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

无论如何 - 我希望这会有所帮助。