.NET IoC模式依赖反转(DIP)、控制反转(Ioc)、依赖注入(DI)

依赖倒置原则(DIP)

依赖倒置(Dependency Inversion Principle,缩写DIP)是面向对象六大基本原则之一。他是指一种特定的的解耦形式,使得高层次的模块不依赖低层次的模块的实现细节,依赖关系被颠倒(反转),从而使得低层次模块依赖于高层次模块的需求抽象.ide

该原则规定:函数

  • 高层次的模块不该该依赖低层次模块,两者都应该依赖其抽象接口.
  • 抽象接口不该该依赖于具体实现,而具体实现则应该依赖于抽象接口.

经过以下一个简单的示例,咱们来看一下,咱们经过一个简单地下单流程向咱们的用户发送相关的短信或者邮件.设计

public SendingEmail
{
    public void Send(string message){
        //do something
    }
}

public Ordering
{
    SendingEmail _sendingEmail=null;
    public void Order(string message){
        //Order business operation
        if(_sendingEmail == null)
        {
            _sendingEmail=new SendingEmail();
        }
        _sendingEmail.Send(message);
    }
}

这样看咱们的代码没问题,目前只要咱们完成了订单操做那么,那么则会触发发送功能,可是他却违反了DIP,由于Ordering类依赖于SendingEmail类,而SendingEmail类不是抽象类,而是一个具体的类.那咱们再来想一个若是这时候业务口的人过来向咱们提出了一个新的需求,要求咱们改成短信而不是Email,那么咱们须要怎么改?对象

public class SendingSMS
{
    public void Send(string message){
        //do something
    }
}
public Ordering
{
    SendingEmail _sendingEmail=null;
    SendingSMS _sendingSMS=null;
    bool isSendingSMS=true;
    public void Order(string message){
        //Order business operation
        if(isSendingSMS){
            if(_sendingSMS == null)
            {
                _sendingSMS=new SendingSMS();
            }
            _sendingSMS.Send(message);
        }else{
            if(_sendingEmail == null)
            {
                _sendingEmail=new SendingEmail();
            }
            _sendingEmail.Send(message);
        }
       
    }
}

根据上述需求咱们不得不建立更多的类,而且在Ordering类中声明他,最后咱们还须要使用IF ELSE语句来决定使用SMS仍是使用电子邮件.可是当咱们有更多这种处理操做后,那么可能比如今还混乱,这就意味着咱们必须在Ordering类中声明更多新的具体类的实例.继承

咱们须要抽离出来一种方式,让高级模块去依赖于抽象,用它来代替咱们实现类,该抽象将映射到实现类.接口

控制反转(IoC)

控制反转(Inversion of Control,缩写为IOC)是面向对象中的设计原则,他能够帮助咱们使高层模块依赖于抽象,而不是底层模块的具体实现.换句话说,他有助于实现(依赖倒置原则——DIP).生命周期

public interface ICustomerCommunication
{
    void Send(string message);
}

而后咱们修改SendingEmail和SendingSMS类以从ICustomerCommunication接口继承.ip

public class SendingEmail:ICustomerCommunication
{
    public void Send(string message){
        //do something
    }
}

public class SendingSMS:ICustomerCommunication
{
    public void Send(string message){
        //do something
    }
}

咱们再来修改一下Ordering类以使用该抽象接口ci

public Ordering
{
    ICustomerCommunication _customerComm=null;
    bool isSendingSMS=true;
    public void Order(string message){
        //Order business operation
        if(isSendingSMS){
            if(_customerComm == null)
            {
                _customerComm=new SendingSMS();
            }
            _customerComm.Send(message);
        }else{
            if(_customerComm == null)
            {
                _customerComm=new SendingEmail();
            }
            _customerComm.Send(message);
        }
       
    }
}

经过如上修改咱们作的控制反转更符合DIP.如今咱们的高级模块只须要依赖于抽象,而不用去依赖实现.get

依赖注入(DI)

依赖注入(Depeondency Injection,缩写为DI)是实现控制反转的一种方式.经常使用的依赖注入方法有3种:

  • 构造函数注入
  • 方法注入
  • 属性注入

虽说经过上面代码咱们实现了IoC,而且Ordering类依赖于ICustomerCommunication抽象,但咱们仍然在Ordering类中使用了实现类,这使用咱们没法在类于类之间彻底解耦.

  if(isSendingSMS){
    if(_customerComm == null)
    {
        _customerComm=new SendingSMS();
    }
        _customerComm.Send(message);
    }else{
        if(_customerComm == null)
        {
            _customerComm=new SendingEmail();
        }
        _customerComm.Send(message);
    }

那咱们再来讲说DI,DI主要帮助咱们将实现注入到抽象的类(ICustomerCommunication接口)中.DI的主要减小类之间的耦合,而且将抽象和具体实现的绑定移除依赖类.

  • 构造函数注入
    经过构造函数注入咱们将实现类的对象传递给依赖类的构造函数,并将其分配给这个接口.
public class Ordering
{
    ICustomerCommunication _customerComm=null;
    public Ordering(ICustomerCommunication customerComm){
        _customerComm=customerComm;
    }
    public void Order(string message){
        _customerComm.Send(message);
    }
}

在上面的代码中,构造函数将采用实现类对象绑定到接口中.若是咱们将SendingSMS的实现传递给这个类,咱们要作的就是声明一个SendingSMS类的实例,而后将其传递给Ordering的构造函数,以下所示:

SendingSMS sendingSMS=new SendingSMS();
Ordering ordering=new Ordering(sendingSMS);
ordering.Order("msg");
  • 方法注入
    经过使用构造函数注入,咱们将不得不在Ordering类的生存期内使用实现类的实例SendingSMS或SendingEmail类.如今若是要在每次调用该方法时传递实现类的实例,则必须使用方法注入.
public class Ordering
{
    public void Order(ICustomerCommunication customerComm,string message){
        _customerComm=customerComm;
        _customerComm.Send(message);
    }
}

调用方式以下所示

SendingSMS sendingSMS=new SendingSMS();
Ordering ordering=new Ordering(sendingSMS);
ordering.Order(sendingSMS,"msg");
  • 属性注入

经过如上描述咱们知道了构造函数注入方法在整个生命周期中使用依赖类,而方法注入是将咱们的注入直接去限于该方法中,而后咱们再去了解一下属性注入

public class Ordering
{
    public ICustomerCommunication customerComm {get;set;}
    public void Order(string message){
        _customerComm.Send(message);
    }
}

调用方式以下所示

SendingSMS sendingSMS=new SendingSMS();
Ordering ordering=new Ordering(sendingSMS);
ordering.customerComm=sendingSMS;
ordering.Order("msg");

其实构造函数注入是实现DI最经常使用的方法.若是须要在每一个方法调用上传递不一样的依赖关系,则可使用方法注入属性注入的使用仍是比较少的.