DIP And DI
依赖倒置(DIP)与依赖注入(DI)
依赖倒置原则(Dependency Inversion Principle)为我们提供了降低模块间耦合度的一种思路,依赖注入(Dependency Injection)是一种具体的实施方法。
依赖倒置原则:
前面一篇讲软件设计原则的文章中已经提到了“依赖倒置原则”(Dependency Inversion Principle),该原则主要是为了降低模块与模块之间的“耦合度”,提倡模块与模块之间不要发生直接的依赖关系,即:高层模块不应该直接依赖于低层模块,高层模块和低层模块应该同时依赖一个抽象层。如果现在有一个类Manager在处理某一任务时,需要记录错误日志,那么我们可以这样编写代码:
class Manager
{
//…
public void DoSomething(ILog logger)
{
try
{
//…
}
catch(Exception ex)
{
logger.Log(ex.ToString());
}
}
}

1 class Manager
2 {
3 //…
4 FileLogger _logger;
5 public void DoSomething()
6 {
7 try
8 {
9 //…do something
10 }
11 catch(Exception ex)
12 {
13 if(_logger == null)
14 {
15 _logger = new FileLogger();
16 }
17 _logger.Log(ex.ToString())
18 }
19 }
20 }
21 class FileLogger
22 {
23 public void Log(string errorLog)
24 {
25 //…write into log file
26 }
27 }

如上代码所示,FileLogger类负责将错误日志保存到文件,Manager类中定义了一个Logger类对象,专门负责记录错误日志,这段代码中的“高层模块”Manager类就直接依赖与“低层模块”FileLogger,如果我们现在需要将错误日志记录通过Email发送给别人,或者发送给别的模块,我们不得不去修改Manager类的代码。
“依赖倒置原则”建议我们,Manager类不应该直接依赖于FIleLogger类,而应该依赖一个抽象层(接口层),所以原来代码应该这样写:
class Manager
{
private ILog _logger;
public ILog Logger
{
get
{
return _logger;
}
set
{
_logger = value;
}
}
//…
}

1 class Manager
2 {
3 ILog _logger;
4 public void DoSomething()
5 {
6 try
7 {
8
9 }
10 catch(Exception ex)
11 {
12 if(_logger == null)
13 {
14 _logger = new FileLogger();
15 // _logger = new EmailLogger();
16 //_logger = new NotifyLogger();
17 }
18 _logger.Log(ex.ToString());
19 }
20 }
21 }
22 interface ILog
23 {
24 void Log(string errorLog);
25 }
26 class FileLogger:ILog
27 {
28 public void Log(string errorLog)
29 {
30 //…write into file
31 }
32 }
33 class EmailLogger:ILog
34 {
35 public void Log(string errorLog)
36 {
37 //…send to others as email
38 }
39 }
40 class NotifyLogger:ILog
41 {
42 public void Log(string errorLog)
43 {
44 //… notify other modules
45 }
46 }

如上代码所示,我们把记录错误日志的逻辑抽象出来一个ILog接口,Manager类不再依赖于任何一个具体的类,而是依赖于ILog接口,同时我们可以根据ILog接口实现各种各样的日志记录类,如FileLogger将日志保存到文件、EmailLogger将日志以邮件形式发送给别人、NotifyLogger将错误信息通知程序中其他模块。这样以来,整个代码的灵活度明显增加了,如果我们需要将日志保存到文件,直接使用FileLogger,如果我们想将日志以邮件形式发送别人,直接使用EmailLogger等等。下图显示依赖倒置发生前后:

依赖注入:
上面的Manager类虽然不再直接依赖任何具体的日志记录类型,但是实质上,我们创建记录日志类对象还是在Manager内部(catch中),如果我们想换种方式记录日志,还是得动Manager类的代码,有没有一种方式,能够让我们不需要修改Manager代码就能切换日志的记录方式呢?当然是有的,“依赖注入”就是这一问题的具体解决方法,我们有三种方式去让两个类型发生依赖关系:
(1)构造注入(Constructor Injection)
在我们创建Manager对象的时候,将记录日志的对象作为构造参数传递给新创建的Manager对象,假设Manager有一个带ILog类型参数的构造方法,如:
class Manager
{
//…
FileLogger _logger;
public void DoSomething()
{
try
{
//…do something
}
catch(Exception ex)
{
if(_logger == null)
{
_logger = new FileLogger();
}
_logger.Log(ex.ToString())
}
}
}
class FileLogger
{
public void Log(string errorLog)
{
//…write into log file
}
}

1 class Manager
2 {
3 ILog _logger;
4 public Manager(ILog logger)
5 {
6 _logger = logger;
7 }
8 //…
9 }

那么,我们在创建Manager对象的时候,这样编写代码:
Manager m = new Manager(new FileLogger());
//Manager m = new Manager(new EmailLogger());
//Manager m = new Manager(new NotifyLogger());
很明显,这种日志记录方式一直不变,对Manager终生有效。
(2)方法注入(Method Injection)
为Manager类中每个需要记录日志的方法增加一个ILog的参数,比如Manager.DoSomething方法重新定义为:
class Manager
{
ILog _logger;
public void DoSomething()
{
try
{
}
catch(Exception ex)
{
if(_logger == null)
{
_logger = new FileLogger();
// _logger = new EmailLogger();
//_logger = new NotifyLogger();
}
_logger.Log(ex.ToString());
}
}
}
interface ILog
{
void Log(string errorLog);
}
class FileLogger:ILog
{
public void Log(string errorLog)
{
//…write into file
}
}
class EmailLogger:ILog
{
public void Log(string errorLog)
{
//…send to others as email
}
}
class NotifyLogger:ILog
{
public void Log(string errorLog)
{
//… notify other modules
}
}

1 class Manager
2 {
3 //…
4 public void DoSomething(ILog logger)
5 {
6 try
7 {
8 //…
9 }
10 catch(Exception ex)
11 {
12 logger.Log(ex.ToString());
13 }
14 }
15 }

那么我们之后在使用Manager的时候,每次调用方法都应该为它提供一个记录日志的对象,如:
Manager m = new Manager();
m.DoSomething(new FileLogger());
m.DoSomething(new EmailLogger());
m.DoSomething(new NotifyLogger());
这种记录日志的方式,只对当前方法有效,每次调用方法都可以不同。
(3)属性注入(Property Injection)
在Manager类中公开一个属性,用来设置日志记录对象,Mananger这样定义:
class Manager
{
ILog _logger;
public Manager(ILog logger)
{
_logger = logger;
}
//…
}

1 class Manager
2 {
3 private ILog _logger;
4 public ILog Logger
5 {
6 get
7 {
8 return _logger;
9 }
10 set
11 {
12 _logger = value;
13 }
14 }
15 //…
16 }

之后我们使用Mananger时,可以随时更换它的日志记录方式:
Mananger m = new Manager();
m.Logger = new FileLogger();
m.Logger = new EmailLogger();
m.Logger = new NotifyLogger();
使用这种方式,我们可以随时切换记录日志的方式,它的灵活度介于“构造注入”和“方法注入”之间。
以上三种依赖注入方法可以混合使用,也就是说,你可以为Manager类定义一个带ILog类型的参数,同时也可以定义一个ILog类型的属性,或者为每个方法增加一个ILog类型的参数。
注:
【1】在.NET中,“抽象层”可以不使用接口interface去实现,而是直接使用委托,举一个例子,我们使用FileStream.BeginRead方法时,给它提供的一个AsyncCallback回调参数,其实就是属于“方法注入”的一种。
【2】类型与类型之间不可能完全失去依赖关系,怎样让这种非有不可的依赖关系更微弱,是软件设计的一门高深学问。
上一篇设计原则(倒数第二小节) http://www.cnblogs.com/xiaozhi_5638/p/3610706.html
DIP And DI的更多相关文章
- Atitit。如何实现dip, di ,ioc ,Service Locator的区别于联系
Atitit.如何实现dip, di ,ioc ,Service Locator的区别于联系 1. Dip原则又来自于松耦合思想方向1 2. 要实现dip原则,有以下俩个模式1 3. Ioc和di的 ...
- 对DIP IoC DI的理解与运用
DIP,IoC,DI基本概念 依赖倒置原则(DIP,Dependency Inverse Principle):强调系统的“高层组件”不应当依赖于“底层组件”,并且不论是“高层组件”还是“底层组件”都 ...
- AutoFac使用~IOC容器(DIP,IOC,DI)
#cnblogs_post_body h1 { background-color: #A5A5A5; color: white; padding: 5px } Autofac一款IOC容器,据说比Sp ...
- dip vs di vs ioc
https://stackoverflow.com/questions/6766056/dip-vs-di-vs-ioc https://docs.microsoft.com/en-us/aspnet ...
- IOC,DIP,DI,IoC容器
定义 IOC(Inversion of Control 控制反转),DIP(Dependency Inverson Principle 依懒倒置)都属于设计程序时指导原则,并没有具体的实现.比较常用 ...
- 一个由正则表达式引发的血案 vs2017使用rdlc实现批量打印 vs2017使用rdlc [asp.net core 源码分析] 01 - Session SignalR sql for xml path用法 MemCahe C# 操作Excel图形——绘制、读取、隐藏、删除图形 IOC,DIP,DI,IoC容器
1. 血案由来 近期我在为Lazada卖家中心做一个自助注册的项目,其中的shop name校验规则较为复杂,要求:1. 英文字母大小写2. 数字3. 越南文4. 一些特殊字符,如“&”,“- ...
- 【Todo】Java学习笔记 100==100 & Reflection API & Optional类详解 & DIP、IoC、DI & token/cookie/session管理会话方式
为什么1000 == 1000返回为False,而100 == 100会返回为True? Link Java Reflection API:Link Java8 Optional 类深度解析: L ...
- 6.在MVC中使用泛型仓储模式和依赖注入实现增删查改
原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-the-generic-repository-pat ...
- 【转】Understanding Inversion of Control, Dependency Injection and Service Locator Print
原文:https://www.dotnettricks.com/learn/dependencyinjection/understanding-inversion-of-control-depende ...
随机推荐
- 百度CSND博客在搜索栏中显示图片
原先以为百度搜索结果有图片是能够人为控制的,结果发现并非这样. 近期百度搜索结果的每一个条目左側出现了小图片,这一变化能够说是极大满足了用户的体验,不用进入站点就提前直观的推断出站点内容是否是自己要找 ...
- c语言获取符号位整数和浮点
1. 为什么你应该得到的签位 非常多的时间,我们需要推断的数目值正和负,做了相应的逻辑处理.完成这一要求条件推断语句可以很好. 有时会出现以下情况, if (x > 0) { x = x - 1 ...
- [Network]Introduction and Basic concepts
[该系列是检讨计算机网络知识.因为现在你想申请出国.因此,在写这篇博客系列的大多数英语.虽然英语,但大多数就是我自己的感受和理解,供大家学习和讨论起来] 1 Network Edge The devi ...
- FMDB与GCD
郝萌主倾心贡献.尊重作者的劳动成果,请勿转载. 假设文章对您有所帮助,欢迎给作者捐赠.支持郝萌主,捐赠数额任意,重在心意^_^ 我要捐赠: 点击捐赠 Cocos2d-X源代码下载:点我传送 因为FMD ...
- Redis实现高并发分布式序列号
使用Redis实现高并发分布式序列号生成服务 序列号的构成 为建立良好的数据治理方案,作数据掌握.分析.统计.商业智能等用途,业务数据的编码制定通常都会遵循一定的规则,一般来讲,都会有自己的编码规则和 ...
- linux_安装 redis
Installation Download, extract and compile Redis with: $ wget http://download.redis.io/releases/redi ...
- WPF - Visual调试工具Snoop
原文:WPF - Visual调试工具Snoop Snoop经过很长一段时间,最近更新到支持NET 3.5了,它是一个WPF运行时对Visual UI调试的一个工具,最近我用过它调试修改过一个bug, ...
- centos 7安装源
参照 http://www.linuxidc.com/Linux/2015-03/114690.htm http://www.cnblogs.com/mchina/archive/2013/01/04 ...
- nolock引发
Sql Server之旅——终点站 nolock引发的三级事件的一些思考 曾今有件事情让我记忆犹新,那年刚来携程不久,马上就被安排写一个接口,供企鹅公司调用他们员工的差旅信息,然后我就三下五除 ...
- Linux下php+mysql+nginx编译搭建(一)
之前一直都是一键搭建的webserver,可是一键搭建的环境相对来说都是比較老的.假设要用比較新的环境,特别是正式server,就必须自己手动编译搭建了(下面搭建基于linux centos6.5 3 ...