什么是控制倒置?

时间:2008-08-06 03:35:27

标签: oop design-patterns inversion-of-control

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

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

38 个答案:

答案 0 :(得分:1312)

控制反转(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类。

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

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

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

这只是一个简单的例子,Simone Busoli的a good series of articles更详细地解释了它。

答案 1 :(得分:548)

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

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

print "enter your name"
read name
print "enter your address"
read address
etc...
store in database

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

在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

所以现在控制被反转...用户不再按照固定顺序接受用户输入,而是控制输入数据的顺序,以及数据保存在数据库中的时间。

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

答案 2 :(得分:388)

什么是控制倒置?

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

  1. 分开时 分开部分。
  2. 确保 部分尽可能关于 部分;反之亦然。
  3. 根据您实施的技术/语言,每种步骤都有几种可能的技术。

    -

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

    -

    实施例

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

答案 3 :(得分:131)

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

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

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

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

答案 4 :(得分:92)

控制倒置(或IoC),关于获得自由(你结婚了,你失去了自由,你被控制了。你离婚了,你刚刚实施了控制倒置。那就是什么我们称之为“脱钩”。良好的计算机系统阻碍了一些非常密切的关系。)更多的灵活性(办公室里的厨房只提供干净的自来水,这是您想喝的唯一选择。老板通过设置一台新的咖啡机实现了控制倒置。现在你可以灵活选择自来水或咖啡。)和减少依赖(你的伴侣有工作,你没有工作,你在经济上依赖你的伴侣,所以你是受控制的。你找到了一份工作,你已经实施了控制反转。良好的计算机系统鼓励依赖。)

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

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

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

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

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

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

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

答案 5 :(得分:82)

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

优点:

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

缺点:

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

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

答案 6 :(得分:62)

  1. Wikipedia Article。对我而言,控制反转正在转变您按顺序编写的代码并将其转换为委托结构。而不是你的程序明确控制一切,你的程序设置一个类或库,具有某些功能,以便在发生某些事情时被调用。

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

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

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

答案 7 :(得分:42)

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

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

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

答案 8 :(得分:40)

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

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

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

所以,显然,当您喜欢这些品种时,IoC是首选。

答案 9 :(得分:37)

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

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

答案 10 :(得分:25)

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

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

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

答案 11 :(得分:20)

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

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

答案 12 :(得分:20)

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

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功能

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

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

作为设计指南的控制反转有以下用途:

将某项任务的执行与实施脱钩 每个模块都可以专注于它的设计目标 模块不会假设其他系统的功能,而是依赖于他们的合同 更换模块对其他模块没有任何副作用。我将在这里保留摘要。您可以访问以下链接以了解该主题的详细信息。
A good read with example

Detailed explanation

答案 13 :(得分:16)

我们说我们在某个酒店开会。

许多人,许多水瓶,许多塑料杯。

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

经过一小时或者其他什么,我们的地板上覆盖着塑料杯和水。

让反转控制。

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

并且她一直向客人提供饮酒。

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

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

这正是Spring(另一个IoC容器,例如:Guice)所做的。使用new关键字(取塑料杯)而不是让应用程序创建它所需要的东西,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

}

有用的链接: -

答案 14 :(得分:16)

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

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

我找到了一个很好的例子here

答案 15 :(得分:15)

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

  

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

如果你发现自己正在复制和粘贴代码,那么你几乎总是在做某些事情错误。编纂为设计原则Once and Only Once

答案 16 :(得分:15)

关于“IoC”这个首字母缩写及其名称的最令人困惑的事情似乎是它的名字太过迷人 - 几乎是一个噪音名称。

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

答案 17 :(得分:11)

控制权反转是指您去杂货店时,您的妻子会向您提供要购买的产品清单。

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

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

答案 18 :(得分:11)

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

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

它说 -

  

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

答案 19 :(得分:11)

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

  • 在正常的s / w开发中,您编写 main() 方法并调用&#34; library&#34;方法。 处于控制之中:)
  • 在IoC中,&#34;框架&#34;控制 main() 并调用您的方法。 框架 处于控制之中:(

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

最终你总是赢了:)

答案 20 :(得分:10)

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

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

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

确定的是以下关系:

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

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

答案 21 :(得分:9)

我找到了一个非常明确的例子here,它解释了“控制是如何反转的”。

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

以下是不使用DI的代码将如何大致工作:

  • 应用程序需要Foo(例如控制器),所以:
  • 应用程序创建Foo
  • 应用程序调用Foo
    • Foo需要Bar(例如服务),所以:
    • Foo创建Bar
    • Foo打电话给Bar
      • Bar需要Bim(服务,存储库......),所以:
      • Bar创建Bim
      • Bar做了一些事情

使用依赖注入

以下是使用DI的代码大致如何工作:

  • 应用程序需要Foo,需要Bar,需要Bim,所以:
  • 应用程序创建Bim
  • 应用程序创建Bar并将其赋予Bim
  • 应用程序创建Foo并为其提供Bar
  • 应用程序调用Foo
    • Foo打电话给Bar
      • 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) {}
}

当您的要求发生变化时,系统会要求您使用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。此外,在测试SomeController类时,构造函数总是期望触发GoogleDriveService类和此类的实际方法。

什么时候适当?什么时候不适合? 在我看来,如果您认为某个类的替代实现(或可能存在),则使用DI。

答案 22 :(得分:8)

编程说话

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

例如,我们说我们有两个类: Dog Cat 。两者都具有相同的品质/状态:年龄,体型,体重。因此,我可以创建一个名为 AnimalService 的单一服务,而不是创建一个名为 DogService CatService 的服务,而只允许使用Dog和Cat如果他们使用界面 IAnimal

然而,实际上,它有一些倒退。

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

b)它有一些限制。例如,让我们谈谈 Dog Cat 的情况,我想仅为狗添加新服务(功能)。让我们说我想计算训练狗需要的天数( trainDays() ),因为猫没用,猫可以&#39训练(我开玩笑)。

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

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

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

答案 23 :(得分:8)

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

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

enter image description here

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

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

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

enter image description here

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

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

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

...

答案 24 :(得分:7)

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

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

无IoC:

*父母:您只能在我问您问题时发言,也只能在我允许您时行事。

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

父母:您想吃饭吗?

孩子:否。

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

孩子 :(想玩,但由于父母没有问题,孩子无法做任何事情。)

1小时后...

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

孩子:是。

父母:已授予权限。

孩子 :(最终可以玩)。

此简单方案说明了控件以父控件为中心。孩子的自由受到限制,在很大程度上取决于父母的问题。要求孩子说话时,孩子可以说话,得到许可后,孩子可以动作。

使用IoC:

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

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

答案 25 :(得分:5)

我知道这里已经给出了答案。但我仍然认为,关于控制反转的一些基础知识必须在这里为未来的读者进行讨论。

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

  

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

这意味着不要去好莱坞实现你的梦想,如果你值得,那么好莱坞会找到你,让你的梦想成真。几乎倒了,是吗?

现在,当我们讨论IoC的原理时,我们会忘记好莱坞。对于IoC来说,必须有三个元素,一个好莱坞,你和一个想实现你梦想的任务。

在我们的编程世界中,好莱坞代表一个通用框架(可能由您或其他人编写),代表您编写的用户代码,任务代表您希望使用代码完成的任务。现在你不能自己去触发你的任务,而不是在IoC!相反,您已经设计了所有内容,以便您的框架将为您触发任务。因此,您构建了一个可重用的框架,可以使某人成为英雄或其他人成为恶棍。但是这个框架总是在负责,它知道什么时候选择某人,而且某人只知道它想成为什么。

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

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

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

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

参考:

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

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

答案 26 :(得分:4)

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

答案 27 :(得分:4)

由于该问题已经有很多答案,但是没有一个显示反转控制术语的细分,因此我看到了给出更简洁,有用的答案的机会。

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

控制反转有三种类型:

界面反转 提供者不应定义接口。取而代之的是,消费者应该定义接口,提供者必须实现它。接口倒置使您无需在每次添加新的提供程序时修改使用者。

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

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

答案 28 :(得分:3)

使用IoC,你不是新手。您的IoC容器将执行此操作并管理它们的生命周期。

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

当您的功能可能在将来发生变化或根据所使用的环境或配置而有所不同时,这是合适的。

答案 29 :(得分:3)

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

要了解有关详情,请阅读我的博客Here

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

    1. 它解决了什么问题?
    2. 在编程方面,IoC尝试通过使其模块化,解耦其各个部分并使其可单元测试来解决单片代码。

      1. 什么时候合适?什么时候不合适?
      2. 大多数情况下这是合适的,除非你有一个只需要单片代码的情况(例如非常简单的程序)

答案 30 :(得分:3)

  1. 所以号码为aboveWhat is Inversion of Control?

  2. 维护是它为我解决的首要问题。它保证我使用接口,以便两个类彼此不亲密。

  3. 在使用Castle Windsor这样的容器时,它可以更好地解决维护问题。能够在没有更改代码行的情况下将转到数据库的组件换成使用基于文件的持久性的组件是非常棒的(配置更改,你已经完成了)。

    一旦你进入泛型,它会变得更好。想象一下,有一个消息发布者接收记录并发布消息。它并不关心它发布的是什么,但它需要一个映射器来从记录到消息。

    public class MessagePublisher<RECORD,MESSAGE>
    {
        public MessagePublisher(IMapper<RECORD,MESSAGE> mapper,IRemoteEndpoint endPointToSendTo)
        {
          //setup
        }
    }
    

    我写了一次,但是如果我发布不同类型的消息,现在我可以在这组代码中注入许多类型。我也可以编写具有相同类型记录的映射器,并将它们映射到不同的消息。在Generics中使用DI使我能够编写很少的代码来完成许多任务。

    哦,是的,有可测性问题,但它们是次要的IoC / DI的好处。

    我绝对喜欢IoC / DI。

    3。一旦你有一个更复杂的中型项目,它就变得更加合适。我会说你开始感到疼痛的那一刻就变得合适了。

答案 31 :(得分:3)

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

答案 32 :(得分:1)

真的不理解为什么会有很多错误的答案,即使被接受也不是很准确,这使得事情很难理解。事实总是简单而干净的。

@Schneider commented中的@Mark Harrison's answer一样,请阅读Martin Fowler讨论IoC的帖子。

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

我最喜欢的之一是:

  

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

为什么?

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

  

控制反转用于提高程序的模块化程度并使其可扩展...然后在2004年由 Robert C. Martin Martin Fowler 进一步普及。 / p>

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

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

答案 33 :(得分:0)

控制权反转是程序中职责转移的指示器。

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

最小的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 dependency
    if ($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);

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

答案 34 :(得分:0)

这是什么?(耦合)控件的反转,更改方法签名的耦合方向。在反向控制下,方法签名的定义由方法实现(而不是方法的调用者)决定。 Full explanation here

它可以解决哪个问题?自上而下地耦合方法。随后,无需进行重构。

什么时候合适,什么时候不合适?对于定义不大的小型应用程序,这些应用程序没有太大的变化,可能会产生开销。但是,对于将要发展的定义不明确的应用程序,它将减少方法签名的固有耦合。这使开发人员有更大的自由来开发应用程序,而无需进行昂贵的代码重构。基本上,只需很少的工作即可使应用程序发展。

答案 35 :(得分:0)

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

答案 36 :(得分:0)

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

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

控制反转:Main与Abstract,以及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/

答案 37 :(得分:0)

控制反转意味着您可以控制组件(类)的行为方式。之所以称其为“反转”,是因为在此模式之前,这些类是硬连接的,并且对它们将要执行的操作有明确的定义。

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

使用IoC,此控件是反向的,即,它给您的方式如何?该库将实现以下内容:

当您在IoC容器中配置内容时,它将具有一个TextEditor类,然后它将具有一个ISpeallChecker(这是一个接口而不是一个具体的SpellChecker类)。在春季,您可以提供自己的“ ISpellChecker”实现,以检查德语的拼写。因此,可以从该库中获得对拼写检查将如何进行的控制,并将其交给您。那就是IoC。