ChatGPT解决这个技术问题 Extra ChatGPT

控制反转与依赖注入

根据paper written by Martin Fowler,控制反转是程序控制流反转的原理:不是程序员控制程序流,而是外部源(框架、服务、其他组件)控制它。就像我们将某些东西插入到其他东西中一样。他提到了一个关于 EJB 2.0 的例子:

例如,会话 Bean 接口定义了 ejbRemove、ejbPassivate(存储到辅助存储)和 ejbActivate(从被动状态恢复)。您无法控制何时调用这些方法,而只能控制它们的作用。容器呼叫我们,我们不呼叫它。

这导致了框架和库之间的区别:

控制反转是使框架与库不同的关键部分。库本质上是一组可以调用的函数,现在通常组织成类。每个调用都会做一些工作并将控制权返回给客户端。

我认为,DI 是 IOC 的观点意味着对象的依赖关系是倒置的:它不是控制自己的依赖关系,而是生命周期......其他东西会为你做。但是,正如你亲手告诉我的 DI,DI 不一定是 IOC。我们仍然可以有 DI 而没有 IOC。

然而,在这篇论文中(来自 pococapsule,另一个 C/C++ 的 IOC 框架),它表明由于 IOC 和 DI,IOC 容器和 DI 框架远优于 J2EE,因为 J2EE 将框架代码混合到组件中,因此不会使其成为普通的旧 Java/C++ 对象 (POJO/POCO)。

Inversion of Control Containers other than the Dependency Injection pattern(存档链接)

附加阅读以了解旧的基于组件的开发框架有什么问题,这导致了上面的第二篇论文:Why and what of Inversion of Control(存档链接)

我的问题:IOC 和 DI 到底是什么?我很困惑。基于 pococapsule,IOC 不仅仅是对象或程序员与框架之间的控制反转更重要的东西。

这是关于这个主题的一篇很好的文章,IoC vs DI(依赖注入) vs SL(服务定位器):tinyurl.com/kk4be58 - 从 url 中提取:IoC vs DI(依赖注入)? IoC 是一般概念,其中流控制从客户端代码反转到框架,“为客户端做一些事情”。 SL(服务定位器)和 DI(依赖注入)是源自 IoC 的两种设计模式。
为了增加我的两分钱,如果有人对依赖注入如何在咖啡店主题中有所帮助感兴趣,我在这里写了一篇文章:digigene.com/design-patterns/dependency-injection-coffeeshop
依赖倒置:依赖于抽象,而不是具体。控制反转:主要与抽象,以及主要如何成为系统的粘合剂。这些是一些谈论这个的好帖子:coderstower.com/2019/03/26/… coderstower.com/2019/04/02/… coderstower.com/2019/04/09/…
深入阅读,它将清除所有martinfowler.com/articles/…

Y
Yadunandan Bhat

 Inversion-of-Control (IoC) 模式是关于提供 任何种类 of callback (“实现”和/或控制反应),而不是直接行动(换句话说,反转和/或将控制重定向到外部处理程序/控制器)。

例如,框架调用应用程序提供的实现,而不是让应用程序调用库(也称为工具包)提供的实现。

 Dependency-Injection (DI) 模式是 IoC 模式的更具体版本,其中实现通过构造函数/设置器/服务查找传递给对象,对象将“依赖” ' 以正确运行。

每个 DI 实现都可以被视为 IoC,但不应将其称为 IoC,因为实现依赖注入比回调更难(不要使用通用术语“IoC”来降低产品的价值)。

例如,不使用 DI 的 IoC 将是模板模式,因为只能通过子类化来更改实现。

DI 框架旨在利用 DI,并且可以定义接口(或 Java 中的 Annotations)以使其易于传递实现。

IoC 容器 是可以在编程语言之外工作的 DI 框架。在某些情况下,您可以配置在侵入性较小的元数据文件(例如 XML)中使用哪些实现。使用一些您可以执行通常不可能的 IoC,例如在 pointcuts 处注入实现。

另请参阅此Martin Fowler's article


感谢你的回答。但另一篇论文认为,对于 IOC,IOC 容器远优于 EJB,而 Martin Fowler 则认为 EJB 是 IOC 的典型示例。
EJB 管理确实是 IoC 的一个典型例子。您可以从 EJB 的生命周期由容器而不是程序员管理这一事实中看出这一点。程序员不会创建或销毁 EJB 实例,因为控制权已委托给服务器。这就是 IoC 的概念:外部代码控制何时调用您的代码,这通常与其大部分时间所做的相反。
IoC 是一个通用术语,意思不是让应用程序调用框架中的方法,而是框架调用应用程序提供的实现。你能解释一下吗?
又名 Hollywood principle,“不要给我们打电话,我们会打电话给你”。将调用留给框架而不是应用程序。
@ImadAlazani,您最好阅读 Garrett 所附的文章,这是关于将控件从应用程序代码反转为框架的详细讨论。
T
Tomasz Jaskuλa

简而言之,IoC 是一个更广泛的术语,包括但不限于 DI

控制反转 (IoC) 一词最初是指整体框架或运行时控制程序流的任何类型的编程风格

在 DI 有名字之前,人们开始将管理依赖的框架称为 Inversion of Control Containers,很快,IoC 的含义逐渐转向了这个特定的含义:Inversion of Control over Dependencies。

控制反转 (IoC) 意味着对象不会创建它们赖以完成工作的其他对象。相反,他们从外部来源(例如,xml 配置文件)获取所需的对象。

依赖注入 (DI) 意味着这是在没有对象干预的情况下完成的,通常由传递构造函数参数和设置属性的框架组件完成。


似乎 IoC 只是依赖倒置原则的另一个术语,不是吗?
@ToddVance - 是的,我认为 IoC 和 DIP 是一回事。 DIP 和 DI 不是一回事。 IoC 可以在没有 DI 的情况下完成,但 DI 不能在没有 IoC 的情况下完成。
@ToddVance - 不,DIP 和 IoC 不是同义词,也不相关。
哈,这就是我在这个线程上的原因......“控制反转与依赖注入”
P
Premraj

https://i.stack.imgur.com/oYukX.jpg

IoC(控制反转):- 这是一个通用术语,并以多种方式实现(事件、委托等)。

DI(依赖注入):- DI 是 IoC 的子类型,通过构造函数注入、setter 注入或接口注入实现。

但是,Spring 仅支持以下两种类型:

Setter 注入 Setter-based DI 是通过在调用无参数构造函数或无参数静态工厂方法来实例化他们的 bean 之后在用户的 bean 上调用 setter 方法来实现的。

Setter-based DI 是通过在调用无参数构造函数或无参数静态工厂方法来实例化他们的 bean 之后在用户的 bean 上调用 setter 方法来实现的。

构造函数注入基于构造函数的 DI 是通过调用带有多个参数的构造函数来实现的,每个参数代表一个协作者。使用它我们可以验证注入的 bean 不为空并且快速失败(编译时失败而不是运行时失败) ,因此在启动应用程序本身时,我们得到 NullPointerException: bean 不存在。构造函数注入是注入依赖项的最佳实践。

基于构造函数的 DI 是通过调用带有多个参数的构造函数来实现的,每个参数代表一个协作者。使用它我们可以验证注入的 bean 不是 null 并且快速失败(在编译时失败而不是在运行时失败),所以在启动应用程序本身时,我们得到 NullPointerException: bean 不存在。构造函数注入是注入依赖项的最佳实践。


说 Spring 不支持属性注入是不正确的。确实如此。这是一种不好的做法,我同意。
Spring @Autowired 注解在我看来是一种属性注入方式
我认为IoC很可能是将对象依赖委托给更高级别的原则,而DI是应用IoC的方式之一
B
Benny Bottema

DI 是 IoC 的子集

IoC 意味着对象不会创建它们赖以完成工作的其他对象。相反,他们从外部服务(例如,xml 文件或单个应用程序服务)获取所需的对象。我使用的 2 个 IoC 实现是 DI 和 ServiceLocator。

DI 意味着获取依赖对象的 IoC 原则是在不使用具体对象而是抽象(接口)的情况下完成的。这使得所有组件链都可测试,因为更高级别的组件不依赖于较低级别的组件,仅依赖于接口。模拟实现了这些接口。

Here are some other techniques to achieve IoC


我不会说 IoC 意味着不创建对象。当您不是直接调用类方法,而是调用接口方法时 - 这是控制反转(因为在这种情况下调用者不依赖于调用代码)并且它根本与对象创建无关。 IoC 的另一个例子是事件和委托
C
CodeFox

IOC(控制反转):将控制权授予容器以获取对象的实例称为控制反转,这意味着您无需使用 new 运算符创建对象,而是让容器为您执行此操作。

DI(依赖注入):将属性注入对象的方式称为依赖注入。

我们有三种类型的依赖注入:

构造函数注入 Setter/Getter 注入 接口注入

Spring 仅支持构造函数注入和 Setter/Getter 注入。


IoC 不需要容器——这只是一种使其更方便的便捷方式。
N
NXT

由于所有答案都强调理论,我想用一个例子来证明:

假设我们正在构建一个应用程序,其中包含在订单发货后发送 SMS 确认消息的功能。我们将有两个类,一个负责发送短信(SMSService),另一个负责捕获用户输入(UIHandler),我们的代码如下所示:

public class SMSService
{
    public void SendSMS(string mobileNumber, string body)
    {
        SendSMSUsingGateway(mobileNumber, body);
    }

    private void SendSMSUsingGateway(string mobileNumber, string body)
    {
        /*implementation for sending SMS using gateway*/
    }
}

public class UIHandler
{
    public void SendConfirmationMsg(string mobileNumber)
    {
        SMSService _SMSService = new SMSService();
        _SMSService.SendSMS(mobileNumber, "Your order has been shipped successfully!");
    }
}

上面的实现没有错,但有几个问题: -) 假设在开发环境中,您希望将发送的短信保存到文本文件而不是使用短信网关来实现这一点;我们最终会用另一个实现改变 (SMSService) 的具体实现,在这种情况下我们失去了灵活性并被迫重写代码。 -) 我们最终会混合类的职责,我们的 (UIHandler) 永远不应该知道 (SMSService) 的具体实现,这应该使用“接口”在类之外完成。当实现这一点时,它将使我们能够通过将使用的(SMSService)与另一个实现相同接口的模拟服务交换来改变系统的行为,该服务会将 SMS 保存到文本文件而不是发送到 mobileNumber。

为了解决上述问题,我们使用将由我们的 (SMSService) 和新的 (MockSMSService) 实现的接口,基本上新的接口 (ISMSService) 将公开两个服务的相同行为,如下面的代码:

public interface ISMSService
{
    void SendSMS(string phoneNumber, string body);
}

然后我们将改变我们的(SMSService)实现来实现(ISMSService)接口:

public class SMSService : ISMSService
{
    public void SendSMS(string mobileNumber, string body)
    {
        SendSMSUsingGateway(mobileNumber, body);
    }

    private void SendSMSUsingGateway(string mobileNumber, string body)
    {
        /*implementation for sending SMS using gateway*/
        Console.WriteLine("Sending SMS using gateway to mobile: 
        {0}. SMS body: {1}", mobileNumber, body);
    }
}

现在我们将能够使用相同的接口创建具有完全不同实现的新模型服务(MockSMSService):

public class MockSMSService :ISMSService
{
    public void SendSMS(string phoneNumber, string body)
    {
        SaveSMSToFile(phoneNumber,body);
    }

    private void SaveSMSToFile(string mobileNumber, string body)
    {
        /*implementation for saving SMS to a file*/
        Console.WriteLine("Mocking SMS using file to mobile: 
        {0}. SMS body: {1}", mobileNumber, body);
    }
}

此时,我们可以更改(UIHandler)中的代码以轻松使用服务(MockSMSService)的具体实现,如下所示:

public class UIHandler
{
    public void SendConfirmationMsg(string mobileNumber)
    {
        ISMSService _SMSService = new MockSMSService();
        _SMSService.SendSMS(mobileNumber, "Your order has been shipped successfully!");
    }
}

我们在代码中实现了很大的灵活性并实现了关注点分离,但我们仍然需要对代码库进行更改以在两个 SMS 服务之间切换。所以我们需要实现依赖注入。

为此,我们需要对 (UIHandler) 类构造函数进行更改以通过它传递依赖项,通过这样做,使用 (UIHandler) 的代码可以确定要使用 (ISMSService) 的哪个具体实现:

public class UIHandler
{
    private readonly ISMSService _SMSService;

    public UIHandler(ISMSService SMSService)
    {
        _SMSService = SMSService;
    }

    public void SendConfirmationMsg(string mobileNumber)
    {
        _SMSService.SendSMS(mobileNumber, "Your order has been shipped successfully!");
    }
}

现在将与类 (UIHandler) 对话的 UI 表单负责传递要使用的接口 (ISMSService) 的实现。这意味着我们已经反转了控件,(UIHandler)不再负责决定使用哪个实现,调用代码负责。我们已经实施了控制反转原理,其中 DI 就是其中的一种。

UI表单代码如下:

class Program
{
    static void Main(string[] args)
    {
        ISMSService _SMSService = new MockSMSService(); // dependency

        UIHandler _UIHandler = new UIHandler(_SMSService);
        _UIHandler.SendConfirmationMsg("96279544480");

        Console.ReadLine();
    }
}

很好的解释
如果您还举了一个没有 di 的 ioc 示例,那就太好了。比如一个基于xml的ioc系统
j
jaco0646

与其直接对比 DI 和 IoC,不如从头开始可能会有所帮助:每个重要的应用程序都依赖于其他代码片段。

所以我正在编写一个类 MyClass,我需要调用 YourService 的一个方法......不知何故,我需要获取一个 YourService 的实例。最简单、最直接的方法是自己实例化它。

YourService service = new YourServiceImpl();

直接实例化是获取依赖项的传统(过程)方式。但它有许多缺点,包括 MyClassYourServiceImpl 的紧密耦合,使我的代码难以更改和测试。 MyClass 不关心 YourService 的实现是什么样的,因此 MyClass 不想负责实例化它。

我更愿意将 MyClass 的责任反转MyClass 之外。最简单的方法就是将实例化调用 (new YourServiceImpl();) 移到其他类中。我可以将这个其他类命名为 Locator、Factory 或任何其他名称;但关键是 MyClass 不再对 YourServiceImpl 负责。我已经颠倒了这种依赖关系。伟大的。

问题是,MyClass 仍然负责调用 Locator/Factory/Whatever。由于我为反转依赖所做的一切就是插入一个中间人,现在我与中间人耦合(即使我没有耦合到中间人给我的具体对象)。

我真的不在乎我的依赖项来自哪里,所以我宁愿不负责调用来检索它们。反转依赖本身是不够的。我想反转对整个过程的控制。

我需要的是 MyClass 插入的完全独立的一段代码(称之为框架)。那么我剩下的唯一责任就是声明我对 YourService 的依赖。该框架可以负责确定在何处、何时以及如何获取实例,并且只需提供 MyClass 所需的内容。最好的部分是 MyClass 不需要了解框架。框架可以控制这个依赖连接过程。现在我已经反转了控制(在反转依赖项之上)。

MyClass 连接到框架有不同的方法。注入就是这样一种机制,我只需声明一个我希望框架提供的字段或参数,通常是在它实例化 MyClass 时。

我认为所有这些概念之间的关系层次结构比该线程中的其他图表显示的要复杂一些;但基本思想是它是一种层次关系。我认为这与 DIP in the wild 同步。

https://i.stack.imgur.com/kIUrS.png


这是一个了不起的答案,@jaco0646。
我喜欢这个答案解决了工厂如何适应所有这些的事实,并提供了一个很好的不同概念的概述图以及它们如何相互关联。
u
user3386493

但是弹簧文档说它们是相同的。

http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-introduction

在第一行“IoC 也称为依赖注入 (DI)”。


我想他们试图解决的是 DI 是一种非常广泛使用的 IoC 设计模式风格,它几乎可以很容易地被称为 IoC aka DI - 除非文档有任何明确的参考建议否则。
“IoC 也被称为依赖注入 (DI)”……马毛!
IoC可以称为设计原则,DI是它的实现。在这两种情况下,管理对象的责任都转移到了 spring 容器中,因此控制反转。
因为这个陈述,面试中的候选人为这个陈述辩护,因为他没有其他信息来源。相当误导...
R
Rahul Gupta

IoC - 控制反转是通用术语,独立于语言,它实际上不是创建对象,而是描述正在创建的时尚对象。

DI - 依赖注入是一个具体的术语,我们在运行时通过使用不同的注入技术来提供对象的依赖关系。 Setter 注入、构造函数注入或通过接口注入。


j
jatin_ghataliya

控制反转是一种设计范式,旨在为应用程序的目标组件提供更多控制权,即完成工作的组件。依赖注入是一种模式,用于创建其他对象所依赖的对象实例,而无需在编译时知道哪个类将用于提供该功能。

有几种基本技术可以实现控制反转。这些是:

使用工厂模式

使用服务定位器模式

使用以下任何给定类型的依赖注入:1)。构造函数注入 2)。二传手注入 3).接口注入


A
Amrit

控制反转是软件架构的通用设计原则,有助于创建易于维护的可重用、模块化软件框架。

这是一种设计原则,其中控制流是从通用编写的库或可重用代码中“接收”的。

为了更好地理解它,让我们看看我们在早期的编码中是如何编码的。在过程/传统语言中,业务逻辑通常控制应用程序的流程并“调用”通用或可重用的代码/功能。例如,在一个简单的控制台应用程序中,我的控制流程由我的程序指令控制,其中可能包括对一些通用可重用函数的调用。

print ("Please enter your name:");
scan (&name);
print ("Please enter your DOB:");
scan (&dob);

//More print and scan statements
<Do Something Interesting>

//Call a Library function to find the age (common code)
print Age

相比之下,对于 IoC,框架是“调用”业务逻辑的可重用代码。

例如,在基于 Windows 的系统中,已经有一个框架可用于创建 UI 元素,如按钮、菜单、窗口和对话框。当我编写应用程序的业务逻辑时,框架的事件将调用我的业务逻辑代码(当事件被触发时),而不是相反。

虽然,框架的代码不知道我的业务逻辑,但它仍然知道如何调用我的代码。这是使用事件/委托、回调等来实现的。这里的流控制是“反转的”。

因此,不是依赖于静态绑定对象的控制流,而是依赖于整个对象图和不同对象之间的关系。

依赖注入是一种设计模式,它实现了 IoC 原则以解决对象的依赖关系。

简而言之,当您尝试编写代码时,您将创建和使用不同的类。一类(A 类)可以使用其他类(B 类和/或 D 类)。因此,B 类和 D 类是 A 类的依赖项。

一个简单的类比是 Car 类。一辆车可能依赖于其他类别,如发动机、轮胎等。

依赖注入建议不是依赖类(此处为类 Car)创建其依赖项(类引擎和类 Tyre),而是应向类注入依赖项的具体实例。

让我们用一个更实际的例子来理解。假设您正在编写自己的 TextEditor。除此之外,您可以拥有一个拼写检查器,为用户提供检查其文本中的拼写错误的工具。这种代码的简单实现可以是:

Class TextEditor
{

    //Lot of rocket science to create the Editor goes here

    EnglishSpellChecker objSpellCheck;
    String text;

    public void TextEditor()

    {   

        objSpellCheck = new EnglishSpellChecker();

    }

    public ArrayList <typos> CheckSpellings()
    {

        //return Typos;

    }

}

乍一看,一切看起来都很美好。用户将编写一些文本。开发人员将捕获文本并调用 CheckSpellings 函数并找到他将向用户显示的错字列表。

一切似乎都很好,直到有一天一位用户开始在编辑器中编写法语。

为了提供对更多语言的支持,我们需要更多的拼写检查器。可能是法语、德语、西班牙语等。

在这里,我们创建了一个紧耦合代码,其中“English”SpellChecker 与我们的 TextEditor 类紧密耦合,这意味着我们的 TextEditor 类依赖于 EnglishSpellChecker,换句话说,EnglishSpellCheker 是 TextEditor 的依赖项。我们需要去除这种依赖。此外,我们的文本编辑器需要一种方法来根据开发人员在运行时的判断来保存任何拼写检查器的具体引用。

因此,正如我们在 DI 的介绍中看到的,它建议类应该注入其依赖项。因此,将所有依赖项注入被调用的类/代码应该是调用代码的责任。所以我们可以将我们的代码重构为

interface ISpellChecker
{

    Arraylist<typos> CheckSpelling(string Text);

}

Class EnglishSpellChecker : ISpellChecker

{

    public override Arraylist<typos> CheckSpelling(string Text)

    {

        //All Magic goes here.

    }

}



Class FrenchSpellChecker : ISpellChecker

{

    public override Arraylist<typos> CheckSpelling(string Text)

    {

        //All Magic goes here.

    }

}

在我们的示例中,TextEditor 类应该接收 ISpellChecker 类型的具体实例。

现在,可以将依赖项注入构造函数、公共属性或方法中。

让我们尝试使用构造函数 DI 更改我们的类。更改后的 TextEditor 类将类似于:

Class TextEditor

{

    ISpellChecker objSpellChecker;

    string Text;



    public void TextEditor(ISpellChecker objSC)

    {

        objSpellChecker = objSC;

    }



    public ArrayList <typos> CheckSpellings()

    {

        return objSpellChecker.CheckSpelling();

    }

}

这样调用代码在创建文本编辑器时可以将适当的 SpellChecker Type 注入到 TextEditor 的实例中。

您可以阅读完整的文章 here


S
SAMUEL

DI 和 IOC 是两种设计模式,主要侧重于提供组件之间的松散耦合,或者只是一种我们解耦对象之间常规依赖关系的方式,使对象之间不紧密。

通过以下示例,我试图解释这两个概念。

以前我们是这样写代码的

Public MyClass{
 DependentClass dependentObject
 /*
  At somewhere in our code we need to instantiate 
  the object with new operator  inorder to use it or perform some method.
  */ 
  dependentObject= new DependentClass();
  dependentObject.someMethod();
}

使用依赖注入,依赖注入器将负责对象的实例化

Public MyClass{
 /* Dependency injector will instantiate object*/
 DependentClass dependentObject

 /*
  At somewhere in our code we perform some method. 
  The process of  instantiation will be handled by the dependency injector
 */ 

  dependentObject.someMethod();
}

上述将控制权交给其他人(例如容器)进行实例化和注入的过程可以称为控制反转,而 IOC 容器为我们注入依赖项的过程可以称为依赖注入。

IOC是程序控制流倒置的原理:程序不是由程序员控制程序的流程,而是通过减少程序员的开销来控制流程。程序注入依赖项的过程称为DI

这两个概念协同工作,为我们提供了一种编写更灵活、可重用和封装的代码的方法,这使它们成为设计面向对象解决方案的重要概念。

也推荐阅读。

What is dependency injection?

您还可以在此处查看我的类似答案之一

Difference between Inversion of Control & Dependency Injection


B
Bruno de Andrade

IOC(Inversion Of Control):将控制权交给容器以获取对象的实例称为Inversion of Control。这意味着不是您使用 new 运算符创建对象,而是让容器为您执行此操作。

DI(依赖注入):将所需的参数(属性)从 XML 传递到对象(在 POJO CLASS 中)称为依赖注入。


K
Kunal

IOC表示一个外部类管理一个应用程序的类,而外部类是一个容器管理应用程序类之间的依赖关系。 IOC 的基本概念是程序员不需要创建您的对象,而是描述应该如何创建它们。

IoC 容器执行的主要任务是:实例化应用程序类。配置对象。组装对象之间的依赖关系。

DI是通过使用setter注入或构造函数注入在运行时提供对象依赖关系的过程。


a
afshar

国际奥委会-DIP-DI

控制反转 (IOC) 依赖反转原理 (DIP) 依赖注入 (DI)

1- IOC:描述一些软件架构设计的一个方面的抽象原则,其中与过程编程相比,系统的控制流是倒置的。

2-DIP:是面向对象编程(OOP)原则(D of SOLID)。

3-DI:是一种实现控制反转并允许程序设计遵循依赖反转原则的软件设计模式。

https://i.stack.imgur.com/4lsz0.jpg


s
supernova

IOC(Inversion of Control)基本上是移除依赖项并将它们解耦以使流程非线性的设计模式概念,并让容器/或另一个实体管理依赖项的供应。它实际上遵循好莱坞的原则“不要打电话给我们,我们会打电话给你”。所以总结一下区别。

控制反转:- 这是一个通用术语,用于解耦依赖关系并委托它们的配置,这可以通过多种方式实现(事件、委托等)。

依赖注入:- DI 是 IOC 的子类型,通过构造函数注入、setter 注入或方法注入来实现。

下面的文章非常巧妙地描述了这一点。

https://www.codeproject.com/Articles/592372/Dependency-Injection-DI-vs-Inversion-of-Control-IO


R
Rich Remer

我认为可以清楚地展示这个想法,而无需陷入面向对象的杂草中,这似乎混淆了这个想法。

// dependency injection
function doSomething(dependency) {
    // do something with your dependency
}

// in contrast to creating your dependencies yourself
function doSomething() {
    dependency = getDependencySomehow()
}

// inversion of control
application = makeApp(authenticate, handleRequest, sendResponse)
application.run(getRequest())

// in contrast to direct control or a "library" style
application = makeApp()
request = application.getRequest()

if (application.authenticate(request.creds)) {
    response = application.handleRequest(request)
    application.sendResponse(response)
}

如果您歪着头眯着眼睛,您会发现 DI 是 IoC 的一种特殊实现,具有特定的关注点。不是将模型和行为注入应用程序框架或高阶操作,而是将变量注入函数或对象。


y
yoAlex5

DIP vs DI vs IoC

[Dependency Inversion Principle(DIP)]SOLID[About] 的一部分,它要求您使用抽象而不是实现

依赖注入 (DI) - 使用聚合而不是组合[About]在这种情况下,外部对象负责内部逻辑。这使您可以拥有更多动态和可测试的方法

class A {
  B b

  //injecting B via constructor 
  init(b: B) {
     self.b = b
  }
}

控制反转(IoC) 非常高级的定义,更多的是关于控制流。最好的例子是 Inversion of Control(IoC) Container or Framework[About]。例如,您没有控件的框架 GUI,您可以做的一切只是实现框架的接口,当框架中发生某些操作时将调用该接口。因此控制权从您的应用程序转移到正在使用的框架中

直插+直插

class A {
  IB ib

  init(ib: IB) {
     self.ib = ib
  }
}

您也可以使用以下方法实现它:

[工厂方法]

[服务定位器]

[IoC 容器(框架)]

更复杂的例子

多层/模块结构中的依赖规则

伪代码:

interface InterfaceInputPort {
    func input()
}

interface InterfaceOutputPort {
    func output()
}

class A: InterfaceOutputPort {

    let inputPort = B(outputPort: self)

    func output() {
        print("output")
    }
}

class B: InterfaceInputPort {
    let outputPort: InterfaceOutputPort

    init(outputPort: InterfaceOutputPort) {
        self.outputPort = outputPort
    }

    func input() {
        print("input")
    }
}


C
Community

让我们从 SOLID 的 D 开始,看看 Scott Millett 的《Professional ASP.NET Design Patterns》一书中的 DI 和 IoC:

依赖倒置原则 (DIP) DIP 就是将您的类与具体实现隔离开来,让它们依赖于抽象类或接口。它将编码的口头禅推广到接口而不是实现,从而通过确保您不与一个实现紧密耦合来增加系统内的灵活性。依赖注入 (DI) 和控制反转 (IoC) 与 DIP 密切相关的是 DI 原理和 IoC 原理。 DI 是通过构造函数、方法或属性提供低级或依赖类的行为。与 DI 一起使用,这些依赖类可以转换为接口或抽象类,这将导致高度可测试且易于更改的松散耦合系统。在 IoC 中,与过程编程相比,系统的控制流是相反的。这方面的一个例子是 IoC 容器,其目的是将服务注入客户端代码,而无需客户端代码指定具体实现。在这个实例中被反转的控制是客户端获取服务的行为。

米利特,C(2010 年)。专业的 ASP.NET 设计模式。威利出版社。 7-8。


V
Vaibs

//ICO,DI,10 年前,这就是他们的方式:

public class  AuditDAOImpl implements Audit{

    //dependency
    AuditDAO auditDAO = null;
        //Control of the AuditDAO is with AuditDAOImpl because its creating the object
    public AuditDAOImpl () {
        this.auditDAO = new AuditDAO ();
    }
}

现在有了 Spring 3,4 或最新版本,如下所示

public class  AuditDAOImpl implements Audit{

    //dependency

     //Now control is shifted to Spring. Container find the object and provide it. 
    @Autowired
    AuditDAO auditDAO = null;

}

总体而言,控件从耦合代码的旧概念反转为使对象可用的 Spring 等框架。这就是我所知的 IOC 和当我们使用 Constructor 或 setter 将依赖对象注入另一个对象时所知道的依赖注入。注入基本上意味着将其作为参数传递。在 Spring 中,我们有基于 XML 和注解的配置,我们在其中定义 bean 对象并使用 Constructor 或 setter 注入样式传递依赖对象。


S
Sachindra N. Pandey

我在 Dzone.com 上找到了最好的例子,这对于理解 IOC 和 DI 之间的真正区别非常有帮助

“IoC 就是当你让其他人为你创建对象时。”因此,不要在代码中编写“new”关键字(例如,MyCode c=new MyCode()),而是由其他人创建对象。这个“其他人”通常被称为 IoC 容器。这意味着我们将 rrsponsibility(控制)移交给容器以获取对象的实例,这称为控制反转。这意味着您无需使用 new 运算符创建对象,而是让容器为您执行此操作。

   DI(Dependency Injection):  Way of injecting properties to an object is 
   called 
  Dependency injection.
   We have three types of Dependency injection
    1)  Constructor Injection
    2)  Setter/Getter Injection
    3)  Interface Injection
   Spring will support only Constructor Injection and Setter/Getter Injection.

Read full article IOCRead Full article DI


A
Abhishek

1) DI 是 Child->obj 依赖于 parent-obj。动词取决于很重要。 2)IOC是Child->obj在一个平台下执行。平台可以是学校、学院、舞蹈课。这里 perform 是在任何平台提供商下具有不同含义的活动。

实际示例:`

//DI
child.getSchool();
//IOC
child.perform()// is a stub implemented by dance-school
child.flourish()// is a stub implemented by dance-school/school/

`

-AB


H
Hearen

至于这个问题,我想说wiki已经提供了详细易懂的解释。我将在这里引用最重要的部分。

Implementation of IoC

在面向对象编程中,有几种基本技术可以实现控制反转。它们是: 使用服务定位器模式 使用依赖注入,例如构造函数注入 参数注入 设置器注入 接口注入;使用上下文查找;使用模板方法设计模式;使用策略设计模式

至于Dependency Injection

依赖注入是一种技术,其中一个对象(或静态方法)提供另一个对象的依赖项。依赖项是可以使用的对象(服务)。注入是将依赖项传递给将使用它的依赖对象(客户端)。


F
Fahim Farook

IoC 概念最初是在过程编程时代听到的。因此,从历史背景来看,IoC 谈到了控制流所有权的反转,即谁负责按所需顺序调用函数——无论是函数本身还是应该将其反转为某个外部实体。

然而,一旦 OOP 出现,人们开始在 OOP 上下文中谈论 IoC,其中应用程序除了控制流之外,还关注对象创建及其关系。此类应用程序希望反转对象创建(而不是控制流)的所有权,并且需要一个容器来负责对象创建、对象生命周期和注入应用程序对象的依赖关系,从而消除应用程序对象创建其他具体对象的过程。

从这个意义上说,DI 与 IoC 不同,因为它与控制流无关,但它是一种 Io*,即对象创建的所有权反转。

What is wrong in my way of explainning DI and IoC?