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

对DIP IoC DI的理解与运用的更多相关文章

  1. 6. Laravel5学习笔记:IOC/DI的理解

    介绍 IOC 控制反转 Inversion of Control 依赖关系的转移 依赖抽象而非实践 DI 依赖注入 Dependency Injection 不必自己在代码中维护对象的依赖 容器自己主 ...

  2. 我对IoC/DI的理解

    IoC IoC: Inversion of Control,控制反转, 控制权从应用程序转移到框架(如IoC容器),是框架共有特性 1.为什么需要IoC容器 1.1.应用程序主动控制对象的实例化及依赖 ...

  3. Spring系列(二):Spring IoC/DI的理解

    这几天重新学习了一下Spring,在网上找了相关的ppt来看,当看到Spring IoC这一章节的时候,先大致浏览了一下内容,有将近50页的内容,内心窃喜~QAQ~,看完这些内容能够对IoC有更深层次 ...

  4. Spring.Net---3、IoC/DI深入理解

    ------------------------------------------------------------------------ 理解IoC/DI 1.控制反转 --> 谁控制谁 ...

  5. AutoFac使用~IOC容器(DIP,IOC,DI)

    #cnblogs_post_body h1 { background-color: #A5A5A5; color: white; padding: 5px } Autofac一款IOC容器,据说比Sp ...

  6. spring Ioc/DI的理解

    学习spring的童鞋都知道,spring中有两个非常重要的点,Ioc(控制反转)与DI(依赖注入),对于初级玩家来说,这两个概念可能有点模棱两可的感觉,今天就谈下自己的一点理解,不足请多多指教!!! ...

  7. IoC/DI

    From:http://jinnianshilongnian.iteye.com/blog/1471944 我对IoC/DI的理解 博客分类: spring杂谈 IoCDI  IoC IoC: Inv ...

  8. IoC和DI的理解

    1 概述 当我们想闭上眼睛想如何让我们的软件更加可用可维护时,我们总能想到一个词:松耦合.在这篇文章中,主要讲述了模块间存在的依赖关系,但这种依赖关系违背了依赖倒置原则.在这之后,我们将讨论一种解除软 ...

  9. Atitit。如何实现dip, di ,ioc ,Service Locator的区别于联系

    Atitit.如何实现dip, di ,ioc  ,Service Locator的区别于联系 1. Dip原则又来自于松耦合思想方向1 2. 要实现dip原则,有以下俩个模式1 3. Ioc和di的 ...

随机推荐

  1. Asp.Net MVC<三> : ASP.NET MVC 基本原理及项目创建

    MVC之前的那点事儿系列 解读ASP.NET 5 & MVC6系列 MVC模拟(摘自ASP.NET MVC5框架揭秘) Asp.net中,通过HttpModule的形式定义拦截器,也就是路由表 ...

  2. yii2.0归档安装方法

    我前几天用composer安装 一直没成功  我就用归档的方法安装了  所以这篇文字只帮助那些用归档方法安装的朋友 Yii是一个高性能的,适用于开发WEB2.0应用的PHP框架. Yii自带了丰富的功 ...

  3. python 中文乱码问题2

    1.文件存为utf-8的格式,windows和linux两个环境都可以用 2.中文输出可以采用u方法 比如:print u'成年'

  4. SingleTon单例模式总结篇

    在Java设计模式中,单例模式相对来说算是比较简单的一种构建模式.适用的场景在于:对于定义的一个类,在整个应用程序执行期间只有唯一的一个实例对象. 一,懒汉式: 其特点是延迟加载,即当需要用到此单一实 ...

  5. python异常处理

    1. 异常 (1) 异常处理 #没有异常处理 num = int('abc') print(num) #报错 #异常处理 try: num = int('abc') #try里的代码是受保护的 pri ...

  6. 通用PE工具箱 4.0精简优化版

    通用PE工具箱 4.0精简优化版 经用过不少 WinPE 系统,都不是很满意,普遍存在篡改主页.添加广告链接至收藏夹.未经允许安装推广软件等流氓行为,还集成了诸多不常用的工具,令人头疼不已.那么今天给 ...

  7. Redis总结(四)Redis 的持久化

    前面已经总结了Redis 的安装和使用今天讲下Redis 的持久化. redis跟memcached类似,都是内存数据库,不过redis支持数据持久化,也就是说redis可以将内存中的数据同步到磁盘来 ...

  8. Android高手速成--第三部分 优秀项目

    主要介绍那些Android还不错的完整项目,目前包含的项目主要依据是项目有意思或项目分层规范比较好.Linux项目地址:https://github.com/torvalds/linuxAndroid ...

  9. 如何使用C自带的qsort快速排序

    / you can write to stdout for debugging purposes, e.g. // printf("this is a debug message\n&quo ...

  10. Javascript中闭包问题(转载)

    学习Javascript闭包(Closure)   作者: 阮一峰 日期: 2009年8月30日 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现 ...