当第一次遇到控制反转(IoC)时,它可能非常令人困惑。
这是怎么一回事?它解决了哪个问题?什么时候使用合适,什么时候不合适?
当第一次遇到控制反转(IoC)时,它可能非常令人困惑。
这是怎么一回事?它解决了哪个问题?什么时候使用合适,什么时候不合适?
当前回答
我在使用库或框架的上下文中想到了控制反转。
传统的“控制”方式是我们构建一个控制器类(通常是主类,但也可以是任何类型的),导入一个库,然后使用您的控制器类来“控制”软件组件的操作。就像你的第一个C/Python程序(继HelloWorld之后)。
import pandas as pd
df = new DataFrame()
# Now do things with the dataframe.
在这种情况下,我们需要知道Dataframe是什么才能使用它。您需要知道要使用什么方法,它的取值方式等等。如果您通过多态性将它添加到自己的类中,或者只是重新调用它,那么您的类将需要Dataframe库才能正常工作。
“控制反转”意味着过程反转。您可以注册类并将其发送回要控制的引擎,而不是您的类控制库、框架或引擎的元素。换句话说,IoC可能意味着我们正在使用代码来配置框架。您也可以将其视为类似于我们在map或filter中使用函数来处理列表中的数据的方式,只是将其应用于整个应用程序。
如果您是构建引擎的人,那么您可能正在使用依赖注入方法(如上所述)来实现这一点。如果你是一个使用引擎的人(更常见),那么你应该能够只声明类,添加适当的符号,让框架为你完成剩下的工作(例如创建路由、分配servlet、设置事件、输出小部件等)。
其他回答
控制反转意味着您控制组件(类)的行为。为什么称之为“反转”,因为在这种模式之前,类是硬连线的,并且确定了它们将要做什么。
导入具有TextEditor和SpellChecker类的库。现在,这个拼写检查器自然只检查英语的拼写。假设您希望TextEditor处理德语并能够进行拼写检查,那么您可以控制它。
在IoC的情况下,这种控制是反向的,即它是如何给你的?库将实现如下内容:
它将有一个TextEditor类,然后会有一个ISpellChecker(它是一个接口,而不是一个具体的SpellChecker类),当您在IoC容器中配置东西时,例如Spring,您可以提供自己的“ISpellChecker”实现,它将检查德语的拼写。因此,拼写检查将如何工作的控制权是从该库中获取并交给您的。这是IoC。
当你去杂货店,你妻子给你一份要买的产品清单时,就是控制权倒置。
在编程方面,她将回调函数getProductList()传递给正在执行的函数-doShopping()。
它允许函数的用户定义函数的某些部分,使其更加灵活。
控制反转(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类型的全局使用的实例(服务)(这可能不存储所述实例,而是一次又一次地询问提供者)。
我喜欢这样的解释:http://joelabrahamsson.com/inversion-of-control-an-introduction-with-examples-in-net/
它开始很简单,还显示了代码示例。
消费者X需要被消费的类Y来完成某件事。这一切都很好,很自然,但X真的需要知道它使用Y吗?
如果X知道它使用的东西具有Y的行为、方法、财产等,而不知道谁真正实现了这些行为,这还不够吗?
通过提取X在Y中使用的行为的抽象定义(如下面的I所示),并让消费者X使用该行为的实例而不是Y,它可以继续做它所做的事情,而不必知道Y的细节。
在上图中,Y实现了I,X使用了I的一个实例。虽然很可能X仍然使用Y,但有趣的是X并不知道这一点。它只知道它使用了实现I的东西。
阅读文章了解更多信息和好处描述,如:
X不再依赖于Y更灵活,可以在运行时决定实现隔离代码单元,更容易测试
...
为了理解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/