控制反转与依赖注入

Inversion of Control vs Dependency Injection

根据Martin Fowler的论文,控制反转是程序控制流程反转的原则:程序的控制流程不是由程序员控制的,而是由外部源(框架、服务、其他组件)控制的。就像我们把东西插进其他东西一样。他提到了一个关于EJB2.0的例子:

For example the Session Bean interface
defines ejbRemove, ejbPassivate
(stored to secondary storage), and
ejbActivate (restored from passive
state). You don't get to control when
these methods are called, just what
they do. The container calls us, we
don't call it.

这导致了框架和库之间的差异:

Inversion of Control is a key part of
what makes a framework different to a
library. A library is essentially a
set of functions that you can call,
these days usually organized into
classes. Each call does some work and
returns control to the client.

我认为,DI是IOC的观点,意味着一个对象的依赖性是颠倒的:相反,它控制着它自己的依赖性,生命周期…还有别的东西可以帮你。但是,正如你亲手告诉我的,DI不一定是IOC。我们仍然可以有DI,没有IOC。

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

依赖注入模式以外的控制容器反转(存档链接)

附加阅读了解旧的基于组件的开发框架的问题是什么,这导致了上面的第二篇论文:控制反转的原因和内容(归档链接)

我的问题是:国际奥委会和DI究竟是什么?我很困惑。基于PocoCapsule,IOC比在对象或程序员和框架之间反转控制更重要。


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

DI是IOC的一种形式,实现通过构造函数/设置器/服务查找传递到对象中,对象将"依赖"这些实现,以便正确地运行。

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

DI框架被设计为使用DI,并且可以定义接口(或Java中的注解)以便于在实现中传递。

IOC容器是可以在编程语言之外工作的DI框架。在某些情况下,您可以配置哪些实现用于入侵性较低的元数据文件(例如XML)。有了一些可以做IOC,这通常是不可能的,比如在切入点注入一个实现。

另请参阅马丁·福勒的这篇文章。


简而言之,国际奥委会是一个更广泛的术语,包括但不限于

控制反转(IOC)一词最初是指任何一种编程风格,其中框架或运行时控制程序流

在DI命名之前,人们开始将管理依赖性的框架称为反转。在控制容器中,很快,ioc的意义就逐渐转向了那个特定的意义:依赖性控制的反转。

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

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


DI是国际奥委会的一个子集

  • 国际奥委会的意思是物体不会创造出他们赖以工作的其他物体。相反,它们从外部服务(例如,XML文件或单个应用服务)获取所需的对象。我使用的2个IOC实现是DI和服务定位器。
  • DI意味着获得依赖对象的IOC原则是不使用具体对象而使用抽象(接口)。这使得所有组件链都是可测试的,因为更高级别的组件不依赖于较低级别的组件,只依赖于接口。模拟实现这些接口。

以下是实现国际奥委会的其他一些技巧。


enter image description here来源

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

DI(依赖注入):-DI是IOC的一个子类型,由构造函数注入、setter注入或接口注入实现。

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

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

IOC(控制器反转):控制容器以获取对象的实例称为控制反转。这意味着,不要使用新的操作符创建对象,而是让容器为您这样做。

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

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

Spring只支持构造函数注入和setter/getter注入。


因为所有的答案都强调理论,所以我想用一个示例第一的方法来证明:

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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!");
    }
}

上述实施并不错误,但存在一些问题:-)假设在开发环境中,您希望将发送到文本文件的smss保存下来,而不是使用sms网关,以实现这一点;我们最终将使用另一个实现来更改(smsseservice)的具体实现,在这种情况下,我们将失去灵活性并被迫重写代码。-)我们将最终混合类的职责,我们的(uihandler)永远不应该知道(smsseservice)的具体实现,这应该在使用"接口"的类之外完成。实现后,它将使我们能够通过与实现相同接口的另一个模拟服务交换使用的(smsse服务)来更改系统行为,此服务将把smss保存到文本文件中,而不是发送到mobilenumber。

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

1
2
3
4
public interface ISMSService
{
    void SendSMS(string phoneNumber, string body);
}

然后我们将更改(smsservice)实现以实现(ismsservice)接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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)中的代码轻松更改为使用服务(mocksmsseservice)的具体实现,如下所示:

1
2
3
4
5
6
7
8
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)的具体实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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是其中的一种。

用户界面表单代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
class Program
{
    static void Main(string[] args)
    {
        ISMSService _SMSService = new MockSMSService(); // dependency

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

        Console.ReadLine();
    }
}


但是Spring文档说它们是相同的。

http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/beans介绍

在第一行中,"IOC也称为依赖注入(DI)"。


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

依赖注入是一个具体的术语,在这个术语中,我们使用不同的注入技术,即在运行时提供对象的依赖性。setter注入、构造函数注入或通过接口注入。


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

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

  • 使用工厂模式
  • 使用服务定位器模式
  • 使用以下任何给定类型的依赖项注入:1)。构造函数注入2)。二传手注射3)。界面注入

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

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

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

1
2
3
4
5
6
7
8
9
10
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类的依赖项。好的。

一个简单的类比就是一辆普通车。汽车可能还依赖于其他种类,如发动机、轮胎等。好的。

依赖项注入建议,不要创建依赖项(类引擎和类轮胎)的依赖类(此处为类汽车),类应该注入依赖项的具体实例。好的。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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"拼写检查器与我们的textEditor类紧密耦合,这意味着我们的textEditor类依赖于english spellchecker,换句话说,englishspellcheker依赖于textEditor。我们需要消除这种依赖关系。此外,我们的文本编辑器需要一种方法,根据开发人员在运行时的判断来保存任何拼写检查器的具体引用。好的。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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类将如下所示:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Class TextEditor

{

    ISpellChecker objSpellChecker;

    string Text;



    public void TextEditor(ISpellChecker objSC)

    {

        objSpellChecker = objSC;

    }



    public ArrayList <typos> CheckSpellings()

    {

        return objSpellChecker.CheckSpelling();

    }

}

这样,在创建文本编辑器时,调用代码可以将适当的拼写检查器类型插入到文本编辑器的实例中。好的。

你可以在这里阅读整篇文章好的。好啊。


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

国际奥委会集装箱的主要任务是:实例化应用程序类。配置对象。组装对象之间的依赖项。

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


IOC(控制反转)基本上是一个设计模式概念,它删除依赖项并将它们解耦,使流成为非线性的,并让容器/或另一个实体管理依赖项的供应。它实际上遵循了好莱坞的原则,"不要打电话给我们,我们会打电话给你"。所以总结一下这些差异。

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

依赖注入:DI是IOC的一个子类型,由构造函数注入、setter注入或方法注入实现。

下面的文章非常清楚地描述了这一点。

https://www.codeproject.com/articles/592372/dependency-injection-di-vs-inversion-of-control-io


DI和IOC是两种设计模式,主要关注于在组件之间提供松耦合,或者简单地将对象之间的传统依赖关系分离,从而使对象之间不紧密。

通过下面的例子,我试图解释这两个概念。

以前我们写这样的代码

1
2
3
4
5
6
7
8
9
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();
}

通过依赖注入,依赖注入器将负责对象的实例化。

1
2
3
4
5
6
7
8
9
10
11
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。

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

也建议阅读。

什么是依赖注入?

你也可以在这里查我的一个类似答案

控制反转与依赖注入之间的差异


ioc(控制反转):控制容器以获取对象实例,称为控制反转。这意味着,不要使用新的操作符创建对象,而是让容器为您创建对象。

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


关于这个问题,我想说wiki已经提供了详细和容易理解的解释。这里我只引用最重要的一句话。

国际奥委会的实施

In object-oriented programming, there are several basic techniques to
implement inversion of control. These are:

  • Using a service locator pattern Using dependency injection, for
    example Constructor injection Parameter injection Setter injection
    Interface injection;
  • Using a contextualized lookup;
  • Using template method design pattern;
  • Using strategy design pattern
  • 依赖注入

    dependency injection is a technique whereby one object (or static
    method) supplies the dependencies of another object. A dependency is
    an object that can be used (a service). An injection is the passing of
    a dependency to a dependent object (a client) that would use it.


    //ICO,DI,10年前,就是这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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 ();
        }
    }

    现在有了3、4或最新的弹簧,如下所示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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,当我们使用构造函数或setter将依赖对象注入另一个对象时,您也知道依赖注入。inject基本上意味着将其作为参数传递。在Spring中,我们有基于XML和注释的配置,在配置中定义bean对象,并使用构造函数或setter注入样式传递依赖对象。


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

    "IoC is when you have someone else create objects for you." So instead of writing"new" keyword (For example, MyCode c=new MyCode())in your code, the object is created by someone else. This ‘someone else’ is normally referred to as an IoC container. It means we handover the rrsponsibility (control )to the container to get instance of object is called Inversion of Control.,
    means instead of you are creating object using new operator, let the container do that for you.

    1
    2
    3
    4
    5
    6
    7
    8
       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.

    阅读整篇文章IOC和整篇文章DI


    1)di是子对象->obj取决于父对象。动词depends很重要。2)IOC是儿童->OBJ在平台下执行。平台可以是学校、大学、舞蹈班。这里的perform是一个在任何平台提供者下具有不同含义的活动。

    实例:`

    1
    2
    3
    4
    5
    //DI
    child.getSchool();
    //IOC
    child.perform()// is a stub implemented by dance-school
    child.flourish()// is a stub implemented by dance-school/school/

    `

    -AB


    让我们从Solid的D开始,看看Scott Millett的《专业ASP.NET设计模式》一书中的DI和IOC:

    Dependency Inversion Principle (DIP)

    The DIP is all about isolating your classes from concrete
    implementations and having them depend on abstract classes or
    interfaces. It promotes the mantra of coding to an interface rather
    than an implementation, which increases flexibility within a system by
    ensuring you are not tightly coupled to one implementation.

    Dependency Injection (DI) and Inversion of Control (IoC)

    Closely linked to the DIP are the DI principle and the IoC principle. DI is the act of supplying a low level or dependent class via a
    constructor, method, or property. Used in conjunction with DI, these
    dependent classes can be inverted to interfaces or abstract classes
    that will lead to loosely coupled systems that are highly testable and
    easy to change.

    In IoC, a system’s flow of control is inverted
    compared to procedural programming. An example of this is an IoC
    container, whose purpose is to inject services into client code
    without having the client code specifying the concrete implementation.
    The control in this instance that is being inverted is the act of the
    client obtaining the service.

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