当第一次遇到控制反转(IoC)时,它可能非常令人困惑。

这是怎么一回事?它解决了哪个问题?什么时候使用合适,什么时候不合适?


当前回答

我觉得用这么多先前的答案回答这个问题有点尴尬,但我只是觉得任何答案都没有足够简单地说明这个概念。

所以我们开始。。。

在非IOC应用程序中,您需要对流程进行编码,并在其中包含所有详细步骤。考虑一个创建报告的程序,它将包含设置打印机连接、打印页眉、遍历详细记录、打印页脚、可能执行页面馈送等的代码。

在IOC版本的报告程序中,您将配置一个通用的、可重用的报告类的实例,即一个包含打印报告的过程流但其中没有任何详细信息的类。您提供的配置可能使用DI来指定报告应该调用哪个类来打印标题、报告应该调用什么类来打印详细信息行、,以及Report应该调用什么类来打印页脚。

因此,控制反转来自控制过程,而不是代码,而是包含在一个外部的、可重用的类(Report)中,该类允许您指定或注入(通过DI)报告的详细信息-页眉、详细信息行和页脚。

通过提供不同的细节类集,可以使用同一Report类(控制类)生成任意数量的不同报告。您通过依赖Report类来提供控件,而只是通过注入来指定报表之间的差异,从而实现了控件的反转。

在某些方面,IOC可以与驱动器备份应用程序相比较-备份总是执行相同的步骤,但备份的文件集可能完全不同。

现在具体地回答最初的问题。。。

这是怎么一回事?IOC依赖于一个可重用的控制器类,并提供针对当前问题的详细信息。它解决了哪个问题?防止您必须重述控制流程。什么时候使用合适,什么时候不合适?无论何时创建控制流始终相同且仅更改细节的流程流。在创建一次性自定义流程时,您不会使用它。

最后,IOC不是DI,DI也不是IOC——DI通常可以在IOC中使用(为了说明抽象控制类的细节)。

无论如何,我希望这有帮助。

其他回答

这是怎么一回事?反转(耦合)控制,更改方法签名的耦合方向。对于反向控制,方法签名的定义由方法实现(而不是方法的调用方)决定。此处完整解释

它解决了哪个问题?方法上的自顶向下耦合。这随后消除了重构的需要。

什么时候使用合适,什么时候不合适?对于定义良好的小型应用程序,如果没有太大的变化,可能会产生开销。然而,对于将要发展的定义较少的应用程序,它减少了方法签名的固有耦合。这使开发人员可以更自由地开发应用程序,避免了对代码进行昂贵的重构。基本上,允许应用程序在很少返工的情况下发展。

当你去杂货店,你妻子给你一份要买的产品清单时,就是控制权倒置。

在编程方面,她将回调函数getProductList()传递给正在执行的函数-doShopping()。

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

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

当你使用台式电脑时,你已经从(或者说,被控制)了。你必须坐在屏幕前看着屏幕。用键盘打字,用鼠标导航。一个写得不好的软件会让你更加痛苦。如果你把桌面换成笔记本电脑,那么你的控制就有点颠倒了。你可以轻松地拿着它四处走动。所以现在你可以用电脑控制你的位置,而不是由电脑控制。

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

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

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

尽管工人将项目管理的控制权交给了管理者(框架)。但有一些专业人士帮助是很好的。这就是IoC的真正来源。

具有MVC架构的现代Web应用程序依赖于框架来执行URL路由,并将控制器放置在适当的位置以供框架调用。

依赖注入和控制反转是相关的。依赖注入在微观层面,控制反转在宏观层面。为了完成一顿饭(实现IoC),你必须吃每一口(实现DI)。

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

想象一个父母和孩子彼此交谈。

没有IoC:

*家长:我问你问题时你才能说话,我允许你时你才能行动。

家长:这意味着,如果我不问你,你就不能问我你能不能吃饭、玩、上厕所甚至睡觉。

家长:你想吃吗?

孩子:没有。

家长:好的,我会回来的。等我。

孩子:(想玩,但由于家长没有问题,孩子什么都不会做)。

1小时后。。。

家长:我回来了。你想玩吗?

孩子:是的。

父级:已授予权限。

孩子:(终于可以玩了)。

这个简单的场景解释了控件以父级为中心。孩子的自由受到限制,高度依赖于父母的问题。孩子只有在被要求说话时才能说话,只有在获得许可时才能行动。

使用IoC:

孩子现在有能力提出问题,家长可以回答并获得许可。简单地说就是控制颠倒了!孩子现在可以随时提问,尽管在权限方面仍然依赖于家长,但他不依赖于说话/提问的方式。

从技术上解释,这与console/shell/cmd与GUI交互非常相似。(这是马克·哈里森的答案,排名第二)。在控制台中,您依赖于向您询问/显示的内容,如果不先回答问题,您无法跳转到其他菜单和功能;遵循严格的顺序流程。(编程上这就像一个方法/函数循环)。然而,有了GUI,菜单和功能就被布置好了,用户可以选择所需的任何内容,从而拥有更多的控制权和更少的限制。(以编程方式,菜单在选中并执行操作时具有回调)。

所以上面的数字1。什么是控制反转?维护是它为我解决的首要问题。它保证我使用的是接口,这样两个类就不会彼此亲密。

使用温莎城堡这样的容器,它可以更好地解决维护问题。能够在不更改一行代码的情况下,将一个连接到数据库的组件替换为一个使用基于文件的持久性的组件,这是非常棒的(配置更改完成了)。

一旦你进入泛型,它会变得更好。想象一下,拥有一个接收记录并发布消息的消息发布者。它不在乎它发布了什么,但它需要一个映射器将记录中的内容转换为消息。

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

我写过一次,但现在如果我发布不同类型的消息,我可以向这组代码中注入许多类型。我还可以编写映射器,将相同类型的记录映射到不同的消息。将DI与Generics结合使用使我能够编写很少的代码来完成许多任务。

哦,是的,存在可测试性问题,但它们比IoC/DI的优势更为次要。

我绝对喜欢IoC/DI。

3.当你有一个中等规模的项目时,它会变得更加合适。我会说,当你开始感到疼痛时,它就变得合适了。