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

例如,会话Bean接口 定义ejbRemove, ejbPassivate (存储到二级存储),以及 ejbActivate(从被动恢复 状态)。你不能控制时间 这些方法被调用,只是什么 他们做的事。集装箱召唤我们,我们 别这么说。

这就导致了framework和library的区别:

控制反转是一个关键的部分 框架与框架的区别是什么 图书馆。图书馆本质上是一个 一组你可以调用的函数, 这些日子通常组织成 类。每次调用都要做一些工作 将控制权返回给客户端。

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

然而,在本文中(来自pococapsule, C/ c++的另一个IOC框架),它指出,由于IOC和DI, IOC容器和DI框架要比J2EE优越得多,因为J2EE将框架代码混合到组件中,因此它不是普通的旧Java/ c++对象(POJO/POCO)。

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

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

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


当前回答

DI是IoC的一个子集

IoC means that objects do not create other objects on which they rely to do their work. Instead, they get the objects that they need from an outside service (for example, xml file or single app service). 2 implementations of IoC, I use, are DI and ServiceLocator. DI means the IoC principle of getting dependent object is done without using concrete objects but abstractions (interfaces). This makes all components chain testable, cause higher level component doesn't depend on lower level component, only from the interface. Mocks implement these interfaces.

以下是一些实现IoC的其他技术。

其他回答

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

假设我们正在构建一个应用程序,该应用程序包含在订单发出后发送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!");
    }
}

Above implementation is not wrong but there are few issues: -) Suppose On development environment, you want to save SMSs sent to a text file instead of using SMS gateway, to achieve this; we will end up changing the concrete implementation of (SMSService) with another implementation, we are losing flexibility and forced to rewrite the code in this case. -) We’ll end up mixing responsibilities of classes, our (UIHandler) should never know about the concrete implementation of (SMSService), this should be done outside the classes using “Interfaces”. When this is implemented, it will give us the ability to change the behavior of the system by swapping the (SMSService) used with another mock service which implements the same interface, this service will save SMSs to a text file instead of sending to 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();
    }
}

10c - DIP - DI

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

IOC:描述某些软件架构设计的一个方面的抽象原理,与过程式编程相比,系统的控制流是反向的。

2-DIP:是面向对象编程(Object - Oriented Programming, OOP)原理(SOLID中的D)。

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

IOC和DIP是两个不相交的集合,DIP是DI、服务定位器和其他一些模式的超集合

//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和依赖注入我们使用构造函数或setter将依赖对象注入到另一个对象。注入基本上意味着将它作为参数传递。在spring中,我们有基于XML和注释的配置,我们定义bean对象,并通过构造函数或setter注入样式传递依赖对象。

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

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

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

Setter Injection Setter-based DI is realized by calling setter methods on the user’s beans after invoking a no-argument constructor or no-argument static factory method to instantiate their bean. Constructor Injection Constructor-based DI is realized by invoking a constructor with a number of arguments, each representing a collaborator.Using this we can validate that the injected beans are not null and fail fast(fail on compile time and not on run-time), so while starting application itself we get NullPointerException: bean does not exist. Constructor injection is Best practice to inject dependencies.

IOC(控制反转):将获取对象实例的控制权交给容器称为控制反转,这意味着不是您使用new操作符创建对象,而是让容器为您创建对象。

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

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

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

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