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

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


当前回答

控制反转是用于解耦系统中的组件和层的模式。该模式是通过在构建组件时将依赖项注入组件来实现的。这些依赖性通常作为接口提供,用于进一步去耦和支持可测试性。IoC/DI容器(如Castle Windsor、Unity)是可用于提供IoC的工具(库)。这些工具提供了超越简单依赖管理的扩展功能,包括生存期、AOP/Interception、策略等。a.减轻组件对管理其依赖性的责任。b.提供在不同环境中交换依赖实现的能力。c.允许通过模仿依赖关系来测试组件。d.提供在整个应用程序中共享资源的机制。a.进行测试驱动开发时至关重要。如果没有IoC,很难测试,因为被测组件与系统的其他部分高度耦合。b.开发模块化系统时至关重要。模块化系统是一种无需重新编译即可更换组件的系统。c.如果有许多跨领域的问题需要解决,尤其是在企业应用程序中,则至关重要。

其他回答

对我来说,IoC/DI正在向调用对象推出依赖项。超级简单。

非技术性的答案是,在你打开引擎之前,你可以更换汽车的引擎。如果一切正常(界面),你就很好了。

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

在编程方面,她将回调函数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类型的全局使用的实例(服务)(这可能不存储所述实例,而是一次又一次地询问提供者)。

我同意NilObject,但我想补充一点:

如果你发现自己复制了一个完整的方法,只修改了一小段代码,你可以考虑用控制反转来处理它

如果你发现自己到处复制和粘贴代码,你几乎总是在做错事。编码为“一次且仅一次”的设计原则。

控制反转是将控制权从库转移到客户端。当我们讨论将函数值(lambda表达式)注入(传递)到控制(改变)库函数行为的高阶函数(库函数)中的客户端时,它更有意义。

因此,这个模式的一个简单实现(具有巨大的含义)是一个更高阶的库函数(它接受另一个函数作为参数)。库函数通过赋予客户端提供“控制”函数作为参数的能力来传递对其行为的控制。

例如,“map”、“flatMap”等库函数是IoC实现。

当然,例如,有限的IoC版本是布尔函数参数。客户端可以通过切换布尔参数来控制库函数。

将库依赖项(承载行为)注入到库中的客户端或框架也可以被视为IoC