对DIP IoC DI的理解与运用
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 logsEventLogWriter writer = null;// This function will be called when the app pool has problempublic 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 logsINofificationAction action = null;// This function will be called when the app pool has problempublic 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 logsINofificationAction action = null;// This function will be called when the app pool has problempublic 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 logsINofificationAction action = null;public AppPoolWatcher(INofificationAction concreteImplementation){this.action = concreteImplementation;}// This function will be called when the app pool has problempublic 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 logsINofificationAction action = null;// This function will be called when the app pool has problempublic 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 logsINofificationAction action = null;public INofificationAction Action{get{return action;}set{action = value;}}// This function will be called when the app pool has problempublic 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/p/3791655.html,如需转载请自行联系原作者
对DIP IoC DI的理解与运用相关推荐
- 6. Laravel5学习笔记:IOC/DI的理解
介绍 IOC 控制反转 Inversion of Control 依赖关系的转移 依赖抽象而非实践 DI 依赖注入 Dependency Injection 不必自己在代码中维护对象的依赖 容器自动根 ...
- Spring IOC和DI的理解
Spring框架中离不开Spring的IOC(控制反转) .DI(依赖注入),这里从IOC是什么.IOC能做什么.IOC和DI的区别三个方面介绍下IOC和DI. 一.IOC是什么 IOC容器: 实际上 ...
- 工厂方法模式与IoC/DI
工厂方法模式与IoC/DI IoC--Inversion of Control 控制反转 DI--Dependency Injection 依赖注入 1:如何理解IoC/DI ...
- php 对ioc容器的理解,关于php:了解IoC容器和依赖注入
快进: 我写这篇文章的目的是为了更好地理解依赖项注入和IOC容器,同时也为了以后能够纠正其中的错误,并用它来帮助我的一些朋友了解它们. 到目前为止,我已经尝试阅读各种框架的文档(Laravel.Fue ...
- AutoFac IoC DI 依赖注入
AutoFac IoC DI 依赖注入 记录点点滴滴知识,为了更好的服务后来者! 一.为什么使用AutoFac? 之前介绍了Unity和Ninject两个IOC容器,但是发现园子里用AutoFac的貌 ...
- 手撸Spring系列2:IOC/DI 思想(源码篇-IOC)
说在前头: 笔者本人为大三在读学生,书写文章的目的是为了对自己掌握的知识和技术进行一定的记录,同时乐于与大家一起分享,因本人资历尚浅,发布的文章难免存在一些错漏之处,还请阅读此文章的大牛们见谅与斧正. ...
- 对Spring loC DI的理解
文章转自https://www.cnblogs.com/Mr-Rocker/p/7721824.html,仅供个人学习所用,好东西当然要多多学习啊(#^.^#) 学习过Spring框架的人一定都会听过 ...
- Spring的IOC/DI
一.Spring框架模块结构 Spring Framework是由Spring团队研发的模块化.轻量级开源框架.其主要目的是为了简化项目开发. Test 对应spring-test.jar. Spri ...
- 2.spring IOC(DI)
1. IOC(DI) - 控制反转(依赖注入) 所谓的IOC称之为控制反转,简单来说就是将对象的创建的权利及对象的生命周期的管理过程交由Spring框架来处理,从此在开发过程中不再需要关注对象的创建和 ...
最新文章
- Qt5.8 在windows下mingw静态编译
- c语言如何使四种运算符同级,二 如何学习C语言的运算符和运算顺序
- TensorFlow 莫烦视频学习笔记例子二(一)
- 高仿真的类-ApplicationContextAware
- HTML5 新标签总汇
- 数模国赛要点与注意事项全分享!
- vs2010 学习Silverlight学习笔记(11):数据与通信之WebClient
- Redis进阶篇主从复制----哨兵模式
- 比较两个对象中全部属性值是否相等
- react setState修改嵌套对象
- 从大哥大到iPhone13,谈谈移动网络发展
- 【图像几何】基于matlab GUI插值图像运算【含Matlab源码 850期】
- Android快速入门之使用AdapterView展示不同风格的列表
- 123个微信小程序源码分享(附下载)
- 数学分析教程 第五章学习感受
- 【公众号】如何将公众号给他人开发
- 未明学院学员报告:学会数据分析后,我发现美妆大牌到底哪家强 为什么是江浙沪包邮?
- 计算机桌面图标管理,桌面图标怎么管理 桌面图标管理方法介绍
- PMP第八章:项目质量管理
- wp教程-wp详细教程-免费wordpress模板主题搭建教程