当第一次遇到控制反转(IoC)时,它可能非常令人困惑。
这是怎么一回事?它解决了哪个问题?什么时候使用合适,什么时候不合适?
当第一次遇到控制反转(IoC)时,它可能非常令人困惑。
这是怎么一回事?它解决了哪个问题?什么时候使用合适,什么时候不合适?
当前回答
IoC是关于颠倒代码和第三方代码(库/框架)之间的关系:
在正常的软件开发中,您编写main()方法并调用“library”方法。您可以控制:)在IoC中,“框架”控制main()并调用您的方法。该框架处于受控状态:(
DI(依赖注入)是关于控件在应用程序中如何流动的。传统的桌面应用程序具有从应用程序(main()方法)到其他库方法调用的控制流,但DI控制流是反向的,框架负责启动应用程序、初始化应用程序并在需要时调用方法。
最终,你总会赢:)
其他回答
我在使用库或框架的上下文中想到了控制反转。
传统的“控制”方式是我们构建一个控制器类(通常是主类,但也可以是任何类型的),导入一个库,然后使用您的控制器类来“控制”软件组件的操作。就像你的第一个C/Python程序(继HelloWorld之后)。
import pandas as pd
df = new DataFrame()
# Now do things with the dataframe.
在这种情况下,我们需要知道Dataframe是什么才能使用它。您需要知道要使用什么方法,它的取值方式等等。如果您通过多态性将它添加到自己的类中,或者只是重新调用它,那么您的类将需要Dataframe库才能正常工作。
“控制反转”意味着过程反转。您可以注册类并将其发送回要控制的引擎,而不是您的类控制库、框架或引擎的元素。换句话说,IoC可能意味着我们正在使用代码来配置框架。您也可以将其视为类似于我们在map或filter中使用函数来处理列表中的数据的方式,只是将其应用于整个应用程序。
如果您是构建引擎的人,那么您可能正在使用依赖注入方法(如上所述)来实现这一点。如果你是一个使用引擎的人(更常见),那么你应该能够只声明类,添加适当的符号,让框架为你完成剩下的工作(例如创建路由、分配servlet、设置事件、输出小部件等)。
假设我们在酒店开会。
我们邀请了很多人,所以我们漏掉了很多壶水和很多塑料杯。
当有人想喝水时,他/她将杯子装满,喝水,然后将杯子扔在地板上。
大约一个小时后,我们的地板上覆盖着塑料杯和水。
让我们在反转控件后尝试:
想象一下,在同一地点举行同一次会议,但我们现在有一个服务员只带一个玻璃杯,而不是塑料杯(Singleton)
当有人想喝酒时,服务员会给他们一杯。他们把它喝了,然后还给服务员。
抛开卫生问题不谈,使用服务员(过程控制)更有效、更经济。
这正是Spring(另一个IoC容器,例如:Guice)所做的。Spring IoC没有让应用程序使用新的关键字(例如,拿一个塑料杯子)创建所需的东西,而是为应用程序提供所需对象(一杯水)的同一杯子/实例(singleton)。
把自己想象成这样一个会议的组织者:
示例:-
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://adfjsf.blogspot.in/2008/05/inversion-of-control.htmlhttp://martinfowler.com/articles/injection.htmlhttp://www.shawn-barrett.com/blog/post/Tip-of-the-day-e28093-Inversion-Of-Control.aspx
控制反转(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类型的全局使用的实例(服务)(这可能不存储所述实例,而是一次又一次地询问提供者)。
控制反转意味着您控制组件(类)的行为。为什么称之为“反转”,因为在这种模式之前,类是硬连线的,并且确定了它们将要做什么。
导入具有TextEditor和SpellChecker类的库。现在,这个拼写检查器自然只检查英语的拼写。假设您希望TextEditor处理德语并能够进行拼写检查,那么您可以控制它。
在IoC的情况下,这种控制是反向的,即它是如何给你的?库将实现如下内容:
它将有一个TextEditor类,然后会有一个ISpellChecker(它是一个接口,而不是一个具体的SpellChecker类),当您在IoC容器中配置东西时,例如Spring,您可以提供自己的“ISpellChecker”实现,它将检查德语的拼写。因此,拼写检查将如何工作的控制权是从该库中获取并交给您的。这是IoC。
我觉得用这么多先前的答案回答这个问题有点尴尬,但我只是觉得任何答案都没有足够简单地说明这个概念。
所以我们开始。。。
在非IOC应用程序中,您需要对流程进行编码,并在其中包含所有详细步骤。考虑一个创建报告的程序,它将包含设置打印机连接、打印页眉、遍历详细记录、打印页脚、可能执行页面馈送等的代码。
在IOC版本的报告程序中,您将配置一个通用的、可重用的报告类的实例,即一个包含打印报告的过程流但其中没有任何详细信息的类。您提供的配置可能使用DI来指定报告应该调用哪个类来打印标题、报告应该调用什么类来打印详细信息行、,以及Report应该调用什么类来打印页脚。
因此,控制反转来自控制过程,而不是代码,而是包含在一个外部的、可重用的类(Report)中,该类允许您指定或注入(通过DI)报告的详细信息-页眉、详细信息行和页脚。
通过提供不同的细节类集,可以使用同一Report类(控制类)生成任意数量的不同报告。您通过依赖Report类来提供控件,而只是通过注入来指定报表之间的差异,从而实现了控件的反转。
在某些方面,IOC可以与驱动器备份应用程序相比较-备份总是执行相同的步骤,但备份的文件集可能完全不同。
现在具体地回答最初的问题。。。
这是怎么一回事?IOC依赖于一个可重用的控制器类,并提供针对当前问题的详细信息。它解决了哪个问题?防止您必须重述控制流程。什么时候使用合适,什么时候不合适?无论何时创建控制流始终相同且仅更改细节的流程流。在创建一次性自定义流程时,您不会使用它。
最后,IOC不是DI,DI也不是IOC——DI通常可以在IOC中使用(为了说明抽象控制类的细节)。
无论如何,我希望这有帮助。