对DIP IoC DI的理解与运用

  1. 云栖社区>
  2. 博客列表>
  3. 正文

对DIP IoC DI的理解与运用

ghost丶桃子 2016-05-20 18:41:08 浏览640 评论0

摘要: DIP,IoC,DI基本概念 依赖倒置原则(DIP,Dependency Inverse Principle):强调系统的“高层组件”不应当依赖于“底层组件”,并且不论是“高层组件”还是“底层组件”都应当依赖于抽象。

DIP,IoC,DI基本概念

依赖倒置原则(DIP,Dependency Inverse Principle):强调系统的“高层组件”不应当依赖于“底层组件”,并且不论是“高层组件”还是“底层组件”都应当依赖于抽象。抽象不应当依赖于实现,实现应当依赖于抽象。

依赖(Dependency):组件A如果:①持有B的引用,②调用B的方法,③创建(new)B,则A对B产生依赖。

控制(Control):A依赖B,则B拥有“控制权”,因为B的某种变化可能会引起A的变化。

控制反转(IoC,Inverse of Control):就是将控制权“往高处/上层”转移,控制反转是实现依赖倒置 的一种方法。

依赖注入(DI,Dependency Injection):组件通过构造函数或者setter方法,将其依赖暴露给上层,上层要设法取得组件的依赖,并将其传递给组件。依赖注入是实现控制反转的一种手段。

依赖倒置原则

为了理解什么是DIP?DIP解决了什么问题?先看一个实际的例子,下面的AppPoolWatcher是一个事件通知类,在系统有问题时调用Notify记录日志,日志的记录具体由EventLogWriter来做:

复制代码
class EventLogWriter
{
    public void Write(string message)
    {
        //Write to event log here
    }
}

class AppPoolWatcher
{
    // Handle to EventLog writer to write to the logs
    EventLogWriter writer = null;

    // This function will be called when the app pool has problem
    public void Notify(string message)
    {
        if (writer == null)
        {
            writer = new EventLogWriter();
        }
        writer.Write(message);
    }
}
复制代码

这样的设计可以很好的解决现有的问题,但是这样的设计却违反了DIP。因为高层组件AppPoolWatcher依赖于底层组件EventLogWriter,高层组AppPoolWatcher的实现中有一个底层组件EventLogWriter的引用(高层组件依赖底层组件的实现,而不是依赖抽象)。

这样做会有什么问题?如果需求发生变化,有另一个需求:AppPoolWatcher要把某些问题日志发送email给管理员,那就要再加一个类:EmailSenderAppPoolWatcher中再加一个EmailSender类的引用。当需求再继续加功能时,比如加一个在特定情况下发送短信的类ShortMessageSender,就需要在AppPoolWatcher中再加一个类的引用。

依赖倒置原则要求高层组件AppPoolWatcher不应当依赖底层组件的具体的实现,而应该依赖于一个简单的抽象,这个抽象对应着具体工作的实现。

控制反转

如何实现DIP?这就是控制反转(IoC,Inverse of Control),上面的实现中AppPoolWatcher依赖EventLogWriter,也就是被EventLogWriter“控制”,现在要把这种控制反转一下,看下面的实现:

DIP要求高层组件依赖一个抽象,先提供一个接口用于抽象:

public interface INofificationAction
{
    public void ActOnNotification(string message);
}

现在改变AppPoolWatcher的设计,使其依赖抽象而不是具体实现:

复制代码
class AppPoolWatcher
{
    // Handle to EventLog writer to write to the logs
    INofificationAction action = null;

    // This function will be called when the app pool has problem
    public void Notify(string message)
    {
        if (action == null)
        {
            // Here we will map the abstraction i.e. interface to concrete class 
        }
        action.ActOnNotification(message);
    }
}
复制代码

改变底层组件的实现:

复制代码
class EventLogWriter : INofificationAction
{   
    public void ActOnNotification(string message)
    {
        // Write to event log here
    }
}
复制代码

现在要增加新的功能,比如发送email或者短信,他们的实现可以如下:

复制代码
class EmailSender : INofificationAction
{
    public void ActOnNotification(string message)
    {
        // Send email from here
    }
}

class SMSSender : INofificationAction
{
    public void ActOnNotification(string message)
    {
        // Send SMS from here
    }
}
复制代码

通过如上的设计,就实现了控制反转,现在高层组件不依赖底层组件的具体实现,而是依赖于抽象,底层组件也依赖抽象,这个抽象可以由高层组件定义,这样高层组件就不被底层组件“控制”,从而实现解耦。

依赖注入

在上面AppPoolWatcher的实现中,使用了一个接口INofificationAction,但是这个接口的“实例化”在什么地方做比较好呢?可以像下面这样:

复制代码
class AppPoolWatcher
{
    // Handle to EventLog writer to write to the logs
    INofificationAction action = null;

    // This function will be called when the app pool has problem
    public void Notify(string message)
    {
        if (action == null)
        {
            // Here we will map the abstraction i.e. interface to concrete class 
            writer = new EventLogWriter();
        }
        action.ActOnNotification(message);
    }
}
复制代码

但是这样做又回到了最开始的问题:高层组件AppPoolWatcher内部仍然依赖一个具体的类EventLogWriter。怎么做可以做到更好的解耦,当在需求发生变化的时候,增加新的功能类实现INotificationAction接口的时候可以不用修改AppPoolWatcher这个类?

这就需要用到依赖注入,依赖注入就是要把高层组件依赖的抽象与具体功能类之间的绑定移出到高层组件的实现之外,来进一步减少耦合。

依赖注入主要有三种实现方式:
1 构造函数注入(Constructor injection)
2 方法注入(Method injection)
3 属性注入(Property injection)

构造函数注入

在高层组件的构造函数中注入依赖的类,如下:

复制代码
class AppPoolWatcher
{
    // Handle to EventLog writer to write to the logs
    INofificationAction action = null;

    public AppPoolWatcher(INofificationAction concreteImplementation)
    {
        this.action = concreteImplementation;
    }

    // This function will be called when the app pool has problem
    public void Notify(string message)
    {   
        action.ActOnNotification(message);
    }
}
复制代码

如果AppPoolWatcher要使用EventLogWriter,就可以如下使用:

EventLogWriter writer = new EventLogWriter();
AppPoolWatcher watcher = new AppPoolWatcher(writer);
watcher.Notify("Sample message to log");

这种方式适合高层组件AppPoolWatcher在整个生命周期使用同一个依赖类。

方法注入

在高层组件的方法中注入依赖的类:

复制代码
class AppPoolWatcher
{
    // Handle to EventLog writer to write to the logs
    INofificationAction action = null;

    // This function will be called when the app pool has problem
    public void Notify(INofificationAction concreteAction, string message)
    {
        this.action = concreteAction;
        action.ActOnNotification(message);
    }
}
复制代码

使用如下:

EventLogWriter writer = new EventLogWriter();
AppPoolWatcher watcher = new AppPoolWatcher();
watcher.Notify(writer, "Sample message to log");

可见这种方式比使用构造函数注入的方式要灵活一些,每次只需要在方法中传入不同的功能类,就可以实现不同的功能。

属性注入

属性注入的方式如下:

复制代码
class AppPoolWatcher
{
    // Handle to EventLog writer to write to the logs
    INofificationAction action = null;

    public INofificationAction Action
    {
        get
        {
            return action;
        }
        set
        {
            action = value;
        }
    }

    // This function will be called when the app pool has problem
    public void Notify(string message)
    {   
        action.ActOnNotification(message);
    }
}
复制代码

使用方式如下:

复制代码
EventLogWriter writer = new EventLogWriter();
AppPoolWatcher watcher = new AppPoolWatcher();
// This can be done in some class
watcher.Action = writer;

// This can be done in some other class
watcher.Notify("Sample message to log");
复制代码

属性注入的方式比上面的构造函数注入和方法注入都要灵活,这种方式在“依赖类的注入”和“方法调用”处在不同的模块时会很有用。

推荐文章:

IoC/DIP其实是一种管理思想:http://coolshell.cn/articles/9949.html

 

 

作者:阿凡卢
出处:http://www.cnblogs.com/luxiaoxun/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
http://www.cnblogs.com/luxiaoxun/p/3791655.html

用云栖社区APP,舒服~

【云栖快讯】诚邀你用自己的技术能力来用心回答每一个问题,通过回答传承技术知识、经验、心得,问答专家期待你加入!  详情请点击

网友评论

ghost丶桃子
文章2301篇 | 关注242
关注
立足于对政务信息化的深刻理解,在云计算和大数据技术上持续创新,构筑开放共享、敏捷高效、安全可... 查看详情
是基于语音识别、语音合成、自然语言理解等技术,为企业在多种实际应用场景下,赋予产品“能听、会... 查看详情
Node.js 性能平台(Node.js Performance Platform)是面向中... 查看详情
为您提供简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效... 查看详情
阿里云总监课正式启航

阿里云总监课正式启航