当第一次遇到控制反转(IoC)时,它可能非常令人困惑。
这是怎么一回事?它解决了哪个问题?什么时候使用合适,什么时候不合适?
当第一次遇到控制反转(IoC)时,它可能非常令人困惑。
这是怎么一回事?它解决了哪个问题?什么时候使用合适,什么时候不合适?
当前回答
我知道这里已经给出了答案。但我仍然认为,关于控制反转的一些基础知识必须在这里详细讨论,以供未来读者参考。
控制反转(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
其他回答
什么是控制反转?
如果您遵循这两个简单的步骤,您就完成了控制反转:
把该做什么和什么时候该做分开。确保零件何时尽可能少地了解什么零件;反之亦然。
根据您用于实现的技术/语言,这些步骤中的每一个都有几种可能的技术。
--
控制反转(IoC)的反转部分令人困惑;因为反转是相对项。了解IoC的最好方法是忘记这个词!
--
示例
事件处理。事件处理程序(做什么部分)--引发事件(何时做部分)依赖注入。构造依赖项(做什么部分)的代码——在需要时为客户机实例化和注入依赖项,这通常由Dagger等DI工具来处理(什么时候做部分)。接口。组件客户端(何时执行部分)--组件接口实现(执行部分)x装置夹具。Setup和TearDown(要做的部分)--xUnit框架在开始时调用Setup,在结束时调用TearDown(何时做部分)模板方法设计模式。模板方法何时执行部分--基本子类实现何时执行部分COM.DllMain、DllCanUnload等中的DLL容器方法(做什么部分)--COM/OS(什么时候做部分)
控制反转(IoC)模式是关于提供任何类型的回调,它“实现”和/或控制反应,而不是直接执行自己(换句话说,反转和/或将控制重定向到外部处理器/控制器)。依赖注入(DI)模式是IoC模式的一个更具体的版本,它完全是从代码中删除依赖项。
每个DI实现都可以被视为IoC,但不应该称之为IoC。因为实现依赖注入比回调更困难(不要使用通用术语“IoC”来降低产品的价值)。
例如DI,假设您的应用程序有一个文本编辑器组件,并且您希望提供拼写检查。你的标准代码应该是这样的:
public class TextEditor {
private SpellChecker checker;
public TextEditor() {
this.checker = new SpellChecker();
}
}
我们在这里所做的工作在TextEditor和SpellChecker之间创建了依赖关系。在IoC场景中,我们会这样做:
public class TextEditor {
private IocSpellChecker checker;
public TextEditor(IocSpellChecker checker) {
this.checker = checker;
}
}
在第一个代码示例中,我们正在实例化SpellChecker(this.checker=new SpellCheckr();),这意味着TextEditor类直接依赖于SpellChecker类。
在第二个代码示例中,我们通过在TextEditor的构造函数签名中使用SpellChecker依赖类(而不是在类中初始化依赖项)来创建抽象。这允许我们调用依赖项,然后将其传递给TextEditor类,如下所示:
SpellChecker sc = new SpellChecker(); // dependency
TextEditor textEditor = new TextEditor(sc);
现在,创建TextEditor类的客户端可以控制使用哪个SpellChecker实现,因为我们正在将依赖项注入TextEditor签名中。
注意,就像IoC是许多其他模式的基础一样,上面的示例只是依赖注入类型中的一种,例如:
构造函数注入。IocSpellChecker的实例将自动传递给构造函数,或手动传递给构造函数。沉淀剂注入。IocSpellChecker的实例将通过setter方法或公共属性传递。服务查找和/或服务定位器其中TextEditor将向已知的提供者请求IocSpellChecker类型的全局使用的实例(服务)(这可能不存储所述实例,而是一次又一次地询问提供者)。
假设你是一个物体。然后你去餐馆:
没有IoC:你要求“苹果”,当你要求更多时,你总是得到苹果。
与IoC:你可以要求“水果”。每次上桌你都可以得到不同的水果。例如,苹果、橙子或西瓜。
所以,很明显,当你喜欢品种时,IoC是首选。
控制反转,(或IoC),是关于获得自由(你结婚了,失去了自由,你被控制了。你离婚了,你刚刚实现了控制反转。这就是我们所说的“脱钩”。好的计算机系统阻碍了一些非常亲密的关系。)更灵活(你办公室的厨房只供应干净的自来水,这是你想喝水时的唯一选择。你的老板通过安装一台新的咖啡机实现了控制反转。现在你可以灵活选择自来水或咖啡。)和更少的依赖性(你的伴侣有工作,你没有工作,你在经济上依赖你的伴侣,所以你受到控制。你找到了工作,你实现了控制反转。良好的计算机系统鼓励依赖。)
当你使用台式电脑时,你已经从(或者说,被控制)了。你必须坐在屏幕前看着屏幕。用键盘打字,用鼠标导航。一个写得不好的软件会让你更加痛苦。如果你把桌面换成笔记本电脑,那么你的控制就有点颠倒了。你可以轻松地拿着它四处走动。所以现在你可以用电脑控制你的位置,而不是由电脑控制。
通过实现控制反转,软件/对象消费者可以获得更多的软件/对象控制/选项,而不是被控制或拥有更少的选项。
考虑到上述想法。我们仍然错过了IoC的一个关键部分。在IoC的场景中,软件/对象使用者是一个复杂的框架。这意味着您创建的代码不是自己调用的。现在让我们来解释一下为什么这种方式对web应用程序更有效。
假设您的代码是一组工作人员。他们需要造一辆车。这些工人需要一个地方和工具(软件框架)来制造汽车。一个传统的软件框架就像一个有很多工具的车库。因此,工人们需要自己制定计划,并使用工具来制造汽车。造一辆车不是一件容易的事,工人们很难做好计划并进行适当的合作。一个现代化的软件框架将像一个拥有所有设施和管理人员的现代化汽车工厂。工人不必制定任何计划,管理者(框架的一部分,他们是最聪明的人,制定了最复杂的计划)将帮助协调,以便工人知道何时完成他们的工作(框架调用您的代码)。工人们只需要足够灵活地使用经理给他们的任何工具(通过使用依赖注入)。
尽管工人将项目管理的控制权交给了管理者(框架)。但有一些专业人士帮助是很好的。这就是IoC的真正来源。
具有MVC架构的现代Web应用程序依赖于框架来执行URL路由,并将控制器放置在适当的位置以供框架调用。
依赖注入和控制反转是相关的。依赖注入在微观层面,控制反转在宏观层面。为了完成一顿饭(实现IoC),你必须吃每一口(实现DI)。
IoC是关于颠倒代码和第三方代码(库/框架)之间的关系:
在正常的软件开发中,您编写main()方法并调用“library”方法。您可以控制:)在IoC中,“框架”控制main()并调用您的方法。该框架处于受控状态:(
DI(依赖注入)是关于控件在应用程序中如何流动的。传统的桌面应用程序具有从应用程序(main()方法)到其他库方法调用的控制流,但DI控制流是反向的,框架负责启动应用程序、初始化应用程序并在需要时调用方法。
最终,你总会赢:)