在我所看到的定义中,根本没有使用invertcontrol这两个词来定义“控制反转”。

定义

维基百科


控制反转(IoC)是一种编程技术,在此以面向对象编程的形式表示,其中对象耦合在运行时由汇编程序对象绑定,通常不在使用静态分析的编译时已知。 〜http://en.wikipedia.org/wiki/Inversion_of_control


Martin Fowler



控制反转是Java中的一种常见模式社区,可帮助连接轻型容器或将来自不同项目的组件组装成具有凝聚力的应用程序。 〜基于http://www.martinfowler.com/articles/injection.html(重新命名)



那么,为什么将控制反转命名为控制反转?什么控制权被颠倒了?有没有一种方法可以使用术语“反转和控制”来定义“控制反转”?

评论

可以用简单的英语解释这个问题的人的道具。

如果定义使用了它所定义的词,那将是字典的失败。类似的逻辑也适用于此。

另请参见StackOverflow:什么是控制反转?

@Izkata,字典中通常用短语自己的术语或同义词来定义短语。即:自然力定义在其定义中使用力和自然一词,或者服务术语在规则与术语同义的情况下使用规则和服务一词。

由于这个原因,我更喜欢使用术语“自动依赖项注入”或ADI而不是IoC。福勒本人提到,当前大多数编程范例都具有某种“控制反转”,该术语通常被定义为“将要执行的代码的确定再分配给外部的时间”传统上由程序本身做出这样的确定”。从硬件中断到事件驱动的编程,再到虚拟机和多任务,一切都通过IoC进行。

#1 楼

假设您有某种“存储库”类,该存储库负责将数据从数据源传递给您。

存储库本身可以建立与数据源的连接。但是,如果它允许您通过存储库的构造函数传递到数据源的连接怎么办?

通过允许调用者提供连接,您已将数据源连接依赖项与存储库类解耦,从而允许任何数据源与存储库一起使用,而不仅仅是存储库指定的数据源。

您已经将创建从存储库类到调用者的连接的责任移交给了控制权。

Martin Fowler建议使用术语“依赖注入”来描述这种类型控制反转的概念,因为控制反转的概念不仅可以在构造函数方法中注入依赖项而得到更广泛的应用。

评论


这是依赖注入的一个示例,但是依赖注入只是控制反转的一个子集。还有其他与依赖注入完全无关的东西,它们会实现控制反转。

–dsw88
13年7月22日在18:00

@ mustang2009cobra:谢谢。我所追求的只是一个例子,而不是发生它的实例的完整目录,术语“控制反转”与DI关系最密切,而不是消息传递或其他类似概念。

–罗伯特·哈维(Robert Harvey)
13年7月22日在18:03



的确,依赖注入是IoC的最常见示例,因此DI的示例最有意义。也许对您的答案进行了很小的更改,以指定这是DI示例,这是IoC的最突出类型?

–dsw88
13年7月22日在18:11

这正是我想要的! IoC的许多示例和定义都没有明确说明要反转哪个控件。我意识到IoC不仅有依赖注入,还可以给我带来更多的好处,但是现在终于有一些事情在我脑海中响起。谢谢!

– Korey Hinton
13年7月23日在16:28

#2 楼

我认为没有人能比Martin Fowler更好地解释它,在您所链接的文章的后面。

对于这种新型容器,反转是关于他们如何查找插件实现的。在我朴素的示例中,列表器通过直接实例化查找器实现。这将阻止查找程序成为插件。这些容器使用的方法是确保插件的任何用户都遵循某种约定,该约定允许单独的汇编器模块将实现注入到列表器中。

他在以上段落中解释说,

当这些容器谈论它们因实现“控制反转”而如此有用时,我最终感到非常困惑。控制反转是框架的共同特征,因此说这些轻量级容器之所以特别是因为它们使用控制反转,就好像说我的车之所以特别是因为它有轮子。倒转?当我第一次遇到控件反转时,它位于用户界面的主控件中。早期的用户界面由应用程序控制。您将有一系列命令,例如“输入名称”,“输入地址”;您的程序将驱动提示并获取对每个提示的响应。使用图形(甚至基于屏幕)UI时,UI框架将包含此主循环,而您的程序将为屏幕上的各个字段提供事件处理程序。该程序的主要控制权已被反转,从您移至框架。 br />
因此,我认为我们需要为该模式指定一个更具体的名称。控制反转是一个过于笼统的术语,因此人们会感到困惑。经过与众多IoC倡导者的大量讨论,我们决定使用“依赖注入”这个名称。

为了澄清一点:控制反转意味着将程序的控制结构与经典程序相反的任何东西。设计。
在过去的日子里,一个关键的例子是让框架处理UI和您的代码之间的通信,而不是让代码直接生成UI。
这样的框架几乎占据了主导地位,所以这个问题不再重要了。一个例子是反转对对象实例化的控制。对象实例化(依赖注入)的新术语,但是,在达成协议的时候,“ IoC容器”一词开始流行。是一种特定的依赖性注射功能,但是依赖注入是控制反转的一种特殊类型。这就是为什么无论您身在何处都得到如此困惑的答案的原因。

#3 楼

以下是通常遵循的“常规”控制流程程序:


顺序运行命令
您可以控制程序的控制流程

控件“反转”控制流,这意味着将其翻转:


您的程序不再控制流。而不是按照您认为合适的方式调用命令,而是等待其他人来调用您。

最后一行很重要。别人会在喜欢时给您打电话,而不是在您愿意时给别人打电话。您定义了控制器,但实际上并没有决定何时调用它们。 Rails决定有需要时会打电话给他们。

评论


[在此处插入强制性的“苏联”笑话]

–罗伯特·哈维(Robert Harvey)
13年7月22日在17:35

顺便说一句,我认为您所描述的更像是消息传递,几十年来,消息传递一直是面向对象的。

–罗伯特·哈维(Robert Harvey)
13年7月22日在17:36

@JimmyHoffa-这一点都没错-参见这里。控制反转是比创建依赖项更通用的概念,并且依赖项注入只是IoC的一种形式。

–李
13年7月22日在17:38



@JimmyHoffa:我不同意。这是对控制反转的正确定义,就像您的答案或罗伯特的定义一样,这正是Fowler在创造短语Dependency Injection(这就是你们都称为IoC)时所描述的困惑。

– pdr
13年7月22日在17:39

我同意@pdr的说法:“控制反转意味着从经典的程序设计中反转程序的控制结构的任何事情。”这就是我要描述的,并且是控制的通用反转。依赖注入是IoC的一种,但这并不是唯一实践IoC的东西

–dsw88
13年7月22日在18:04

#4 楼

传统上,当一个类/方法需要使用另一个类(依赖关系)时,它直接由该类/方法实例化。它控制其依赖项。
通过控制反转(IoC),调用方传入了依赖项,因此它(或更高级别的调用方)实例化了依赖项。调用者控制依赖关系。
对依赖关系实例化位置的控制已被反转-而不是位于“底部”(需要代码的地方),而是在“顶部”(实例化)需要它的代码被调用。

评论


@RobertHarvey-平原有多平坦?

–奥德
13年7月22日在16:41

@RobertHarvey呵呵?对我来说,这似乎很简单……什么不简单?单词“实例化”还是“依赖项”?

–吉米·霍法(Jimmy Hoffa)
13年7月22日在17:36



这和上面@ dsw88的帖子对我来说很简单。看,如果我从未听说过IoC,我会直觉地认为“哦,谁来控制某物从一侧翻转到另一侧”。做得好!

–奥利弗·威廉姆斯(Oliver Williams)
17年7月12日在5:45

与您的@RobertHarvey相比,我更喜欢这个答案。

–amn
20年7月12日在19:19

#5 楼

通常,较高级别的代码调用(即控件)较低级别的代码。 Main()调用function(),function()调用libraryFunction()。

这是可以反转的,因此底部的低级库函数将调用较高级的函数。

为什么要这么做?中间件。有时您想控制顶层和底层,但是中间却有很多工作是您不想做的。在C stdlib中实现quicksort。您在顶层调用quicksort。您将qsort()的函数指针传递给您自己的函数,该函数根据您的喜好实现比较器。调用qsort()时,它将调用此比较器函数。 qsort()正在控制/调用/驱动您的高级函数。

#6 楼

这里是一个简单的概述:


控件指的是程序下一步要做的事情
在顶层,通常有两件事控制控件:应用程序本身和用户

在过去,控件首先由应用程序拥有,而用户则由用户拥有。如果应用程序需要用户提供某些东西,它将停止并询问,然后继续执行下一个任务。用户的交互主要提供数据,而不是控制应用程序下一步做什么。如今,这对于我们来说有点陌生,因为我们很少看到这种类型的行为。这意味着,用户无需等待应用程序给它做某事,而是坐在周围等待用户给它做事。 GUI就是一个很好的例子,几乎所有带有事件循环的东西都具有反向控制。应用程序内部的控制(即依赖项注入)。这可能就是为什么很难获得直接答案的原因。

#7 楼

让我们尝试通过两个示例来理解它。

示例1

在早期,用于生成命令提示符的应用程序接连接受用户输入。如今,UI框架实例化了各种UI元素,遍历了这些UI元素的各种事件(例如,鼠标悬停,单击等),并且用户/主程序提供了用于监听这些事件的钩子(例如Java中的UI事件监听器)。因此,主要的UI元素流“控件”已从用户程序移至UI框架。在早期,它在用户程序中。

示例2

考虑以下类CustomerProcessor

class CustomerProcessor
{
    SqlCustRepo custRepo = new SqlCustRepo(); 
    private void processCustomers()
    {
            Customers[] custs = custRepo.getAllCusts();
    }
}


如果我希望processCustomer()独立于任何实现, getAllCusts()的功能,而不仅仅是SqlCustRepo提供的功能,我需要摆脱限制:SqlCustRepo custRepo = new SqlCustRepo()并用更通用的东西代替,能够接受各种实现类型,以便processCustomers()可以简单地用于任何提供的实现。上面的代码(通过主程序逻辑实例化所需的类SqlCustRepo)是一种传统方式,并且无法实现将processCustomers()getAllCusts()的实现分离的目标。在控制反转中,容器实例化所需的实现类(如xml配置所指定),将其注入到主程序逻辑中,该逻辑按照指定的钩子进行绑定(例如通过spring框架中的@Autowired注解或getBean()方法)。 br />
让我们看看如何做到这一点。请考虑以下代码。

Config.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <bean id="custRepo" class="JsonCustRepo" />
</beans>


CustRepo.java

interface ICustRepo 
{ ... }


JsonCustRepo.java

class JsonCustRepo implements CustRepo
{ ... }


App.java

class App
{
    public static void main(String[] args) 
    {
        ApplicationContext context = new ClassPathXmlApplicationContext("Config.xml");
        ICustRepo custRepo = (JsonCustRepo) context.getBean("custRepo");
    }
}


我们也可以拥有

class GraphCustRepo implements ICustRepo { ... }   




<bean id="custRepo" class="GraphCustRepo">


,我们不需要更改App.java。

容器上方(这是Spring框架)负责扫描xml文件,实例化特定类型的bean并将其注入用户程序。用户程序无法控制要实例化哪个类。

PS:IoC是通用概念,可以通过多种方式实现。上面的示例通过依赖项注入来实现。

参考:Martin Fowler的文章。