控制反转与依赖注入

问题描述 投票:430回答:19

根据paper written by Martin Fowler,控制反转是程序控制流被反转的原则:外部源(框架,服务,其他组件)控制它,而不是程序员控制程序的流程。这就像我们把东西塞进别的东西。他提到了一个关于EJB 2.0的例子:

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

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

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

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

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

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

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

我的问题:IOC和DI究竟是什么?我很迷惑。基于pococapsule,IOC比仅仅反转对象或程序员和框架之间的控制更重要。

dependency-injection inversion-of-control
19个回答
552
投票

IoC是一个通用术语,而不是让应用程序在框架中调用方法,框架调用应用程序提供的实现。

DI是IoC的一种形式,其中实现通过构造函数/设置器/服务查找传递到对象,对象将“依赖”以便正确行为。

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

DI框架旨在利用DI并可以定义接口(或Java中的注释),以便于传递实现。

IoC容器是DI框架,可以在编程语言之外工作。在某些情况下,您可以配置哪些实现用于较少侵入性的元数据文件(例如XML)。对于一些你可以做IoC通常是不可能的,就像在pointcuts注入一个实现。

另见Martin Fowler's article


2
投票

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

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

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


2
投票

IOC(控制反转)基本上是设计模式概念,即删除依赖关系并将它们解耦为使流非线性,并让容器/或其他实体管理依赖关系的供应。它实际上跟随好莱坞校长“不要打电话给我们,我们会打电话给你”。总结这些差异。

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

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

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

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


2
投票

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

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

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

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)创建其依赖项(类引擎和类Tire)。

让我们用更实际的例子来理解。请考虑您正在编写自己的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函数,并将找到他将向用户显示的错别字列表。

在一个用户开始在编辑器中编写法语的好日子之前,一切似乎都很有效。

为了提供更多语言的支持,我们需要更多的SpellCheckers。可能是法语,德语,西班牙语等。

在这里,我们创建了一个紧密耦合的代码,其中“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类型的具体实例。

现在,可以在Constructor,Public Property或方法中注入依赖项。

让我们尝试使用Constructor DI更改我们的类。更改后的TextEditor类看起来像:

Class TextEditor

{

    ISpellChecker objSpellChecker;

    string Text;



    public void TextEditor(ISpellChecker objSC)

    {

        objSpellChecker = objSC;

    }



    public ArrayList <typos> CheckSpellings()

    {

        return objSpellChecker.CheckSpelling();

    }

}

这样调用代码在创建文本编辑器时可以将相应的SpellChecker类型注入TextEditor的实例。

你可以阅读完整的文章here


2
投票

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


1
投票

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

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


0
投票

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

依赖倒置原则(DIP)

DIP就是将类与具体实现隔离开来,让它们依赖于抽象类或接口。它将编码的咒语提升到接口而不是实现,这通过确保您没有与一个实现紧密耦合来增加系统内的灵活性。

依赖注入(DI)和控制反转(IoC)

与DIP紧密相关的是DI原理和IoC原则。 DI是通过构造函数,方法或属性提供低级别或依赖类的行为。与DI结合使用时,这些依赖类可以反转为接口或抽象类,这将导致松散耦合的系统高度可测试且易于更改。

在IoC中,与过程编程相比,系统的控制流程被反转。一个例子是IoC容器,其目的是将服务注入客户端代码,而无需客户端代码指定具体实现。在这种情况下被反转的控制是客户获得服务的行为。

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


0
投票

// 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这样的框架,使对象可用。据我所知,当我们使用Constructor或setter将依赖对象注入另一个对象时,我就知道IOC和依赖注入。注入基本上意味着将其作为参数传递。在Spring中,我们有基于XML和注释的配置,我们定义bean对象并使用Constructor或setter注入样式传递依赖对象。


0
投票

我在qazxsw poi上找到了最好的例子,这对于理解IOC和DI之间真正的不同非常有帮助

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

Dzone.com

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 IOC


0
投票

1)DI是Child-> obj取决于parent-obj。动词取决于重要性。 2)IOC是Child-> obj在平台下执行。平台可以是学校,大学,舞蹈班。此处执行是在任何平台提供者下具有不同含义的活动。

实际例子:`

Read Full article DI

`

-AB


0
投票

至于这个问题,我想维基已经提供了详细而易懂的解释。我将在这里引用最重要的内容。

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

在面向对象的编程中,有几种实现控制反转的基本技术。这些是:

  1. 使用服务定位器模式使用依赖注入,例如构造函数注入参数注入Setter注入接口注入;
  2. 使用上下文查找;
  3. 使用模板方法设计模式;
  4. 使用策略设计模式

至于Implementation of IoC

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


193
投票

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

术语“控制反转”(IoC)最初意味着任何类型的编程风格,其中整体框架或运行时控制程序流

在DI有一个名字之前,人们开始引用管理依赖关系作为控制容器反转的框架,很快,IoC的含义逐渐向这个特定含义漂移:控制依赖关系。

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

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


43
投票

DI是IoC的子集

  • IoC意味着对象不会创建他们依赖的其他对象来完成他们的工作。相反,他们从外部服务(例如,xml文件或单个应用程序服务)获取他们需要的对象。我使用的IoC的2个实现是DI和ServiceLocator。
  • DI意味着在不使用具体对象而是使用抽象(接口)的情况下完成获取依赖对象的IoC原则。这使得所有组件链可测试,导致更高级别的组件不依赖于更低级别的组件,仅来自接口。 Mocks实现了这些接口。

Here are some other techniques to achieve IoC


31
投票

enter image description here source

IoC(控制反转): - 它是一个通用术语,以多种方式实现(事件,代理等)。

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

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

  • 二传手注射 基于setter的DI是通过在调用无参数构造函数或无参数静态工厂方法来实例化其bean之后调用用户bean上的setter方法来实现的。
  • 构造函数注入 基于构造函数的DI是通过调用带有多个参数的构造函数来实现的,每个参数代表一个协作者。使用这个我们可以验证注入的bean不是null并且快速失败(编译时失败而不是运行时),所以在启动应用程序本身时,我们得到NullPointerException: bean does not exist。构造函数注入是注入依赖项的最佳实践。

15
投票

IOC(控制器的反转):控制容器以获取对象的实例称为控制反转。意思是代替使用new运算符创建对象,让容器为您执行此操作。

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

We have three types of Dependency injection
    1)  Constructor Injection
    2)  Setter/Getter Injection
    3)  Interface Injection

Spring只支持Constructor Injection和Setter / Getter Injection。


9
投票

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

假设我们正在构建一个应用程序,其中包含一个功能,可以在订单发出后发送SMS确认消息。我们将有两个类,一个负责发送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!");
    }
}

以上实现没有错,但问题很少: - )假设在开发环境中,你想保存发送到文本文件而不是使用SMS网关的短信来实现这一点;我们最终会用另一个实现来改变(SMSService)的具体实现,我们正在失去灵活性并被迫在这种情况下重写代码。 - )我们最终会混合类的职责,我们(UIHandler)永远不应该知道(SMSService)的具体实现,这应该在使用“Interfaces”的类之外完成。实现这一点后,它将使我们能够通过交换与实现相同接口的另一个模拟服务一起使用的(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();
    }
}

5
投票

但是弹簧文档说他们是一样的。

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

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


5
投票

IoC - 控制反转是通用术语,与语言无关,它实际上不是创建对象,而是描述创建时尚对象的方式。

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


4
投票

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

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

  • 使用工厂模式
  • 使用服务定位器模式
  • 使用以下任何给定类型的依赖注入: 1)。构造函数注入 2)。二传手注射 3)。接口注入
© www.soinside.com 2019 - 2024. All rights reserved.