文档章节

依赖倒置(DIP)与依赖注入(DI)

IT周见智
 IT周见智
发布于 2015/06/05 17:17
字数 1493
阅读 11
收藏 0

  依赖倒置原则(Dependency Inversion Principle)为我们提供了降低模块间耦合度的一种思路,依赖注入(Dependency Injection)是一种具体的实施方法。

依赖倒置原则:

  前面一篇讲软件设计原则的文章中已经提到了“依赖倒置原则”(Dependency Inversion Principle),该原则主要是为了降低模块与模块之间的“耦合度”,提倡模块与模块之间不要发生直接的依赖关系,即:高层模块不应该直接依赖于低层模块,高层模块和低层模块应该同时依赖一个抽象层。如果现在有一个类Manager在处理某一任务时,需要记录错误日志,那么我们可以这样编写代码:

 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 }
View Code

如上代码所示,FileLogger类负责将错误日志保存到文件,Manager类中定义了一个Logger类对象,专门负责记录错误日志,这段代码中的“高层模块”Manager类就直接依赖与“低层模块”FileLogger,如果我们现在需要将错误日志记录通过Email发送给别人,或者发送给别的模块,我们不得不去修改Manager类的代码。

  “依赖倒置原则”建议我们,Manager类不应该直接依赖于FIleLogger类,而应该依赖一个抽象层(接口层),所以原来代码应该这样写:

 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 }
View Code

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

 

依赖注入:

  上面的Manager类虽然不再直接依赖任何具体的日志记录类型,但是实质上,我们创建记录日志类对象还是在Manager内部(catch中),如果我们想换种方式记录日志,还是得动Manager类的代码,有没有一种方式,能够让我们不需要修改Manager代码就能切换日志的记录方式呢?当然是有的,“依赖注入”就是这一问题的具体解决方法,我们有三种方式去让两个类型发生依赖关系:

(1)构造注入(Constructor Injection)

  在我们创建Manager对象的时候,将记录日志的对象作为构造参数传递给新创建的Manager对象,假设Manager有一个带ILog类型参数的构造方法,如:

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

那么,我们在创建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方法重新定义为:

 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 }
View Code

那么我们之后在使用Manager的时候,每次调用方法都应该为它提供一个记录日志的对象,如:

  Manager m = new Manager();

  m.DoSomething(new FileLogger());

  m.DoSomething(new EmailLogger());

  m.DoSomething(new NotifyLogger());

这种记录日志的方式,只对当前方法有效,每次调用方法都可以不同。

(3)属性注入(Property Injection)

  在Manager类中公开一个属性,用来设置日志记录对象,Mananger这样定义:

 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 }
View Code

之后我们使用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

© 著作权归作者所有

共有 人打赏支持
IT周见智

IT周见智

粉丝 10
博文 61
码字总数 185891
作品 0
西青
私信 提问
Spring依赖注入基础以及实现原理

一、Spring简介 1.Spring简化Java开发 Spring Framework是一个应用框架,框架一般是半成品,我们在框架的基础上可以不用每个项目自己实现架构、基础设施和常用功能性组件,而是可以专注业务逻...

小贱是个程序员
2016/11/20
602
0
spring依赖注入基础

一、Spring简介 1.Spring简化Java开发 Spring Framework是一个应用框架,框架一般是半成品,我们在框架的基础上可以不用每个项目自己实现架构、基础设施和常用功能性组件,而是可以专注业务逻...

皮蛋瘦肉粥里没有粥
2016/03/03
24
0
个人对【依赖倒置(DIP)】、【控制反转(IOC)】、【依赖注入(DI)】浅显理解

一、依赖倒置(Dependency Inversion Principle) 依赖倒置是面向对象设计领域的一种软件设计原则。(其他的设计原则还有:单一职责原则、开放封闭原则、里式替换原则、接口分离原则,合称S...

稻草堆上打着滚儿
2018/09/26
0
0
.NET Core开发日志——依赖注入

依赖注入(DI)不是一个新的话题,它的出现是伴随着系统解耦的需要而几乎必然产生的。 在SOLID设计原则中,DIP(Dependency inversion principle)——依赖倒置,规定了“需依赖抽象,而非实现”...

Ken.W
2018/07/29
0
0
DI,DIP,IOC的演变历史

此随笔的重点在“Demo分析”一章,以代码的分阶段变化讲述了DI,DIP,IOC的演变,写在前面文字均为铺垫。 希望各位园友拍砖,促使流浪者的进步,现在有很多问题想讨论,即以此文寻找志同道合...

科技小毛
2017/10/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

大数据教程(11.9)hive操作基础知识

上一篇博客分享了hive的简介和初体验,本节博主将继续分享一些hive的操作的基础知识。 DDL操作 (1)创建表 #建表语法CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name [(col_name ...

em_aaron
今天
0
0
OSChina 周四乱弹 —— 我家猫真会后空翻

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @我没有抓狂 :#今天听这个# 我艇牛逼,百听不厌,太好听辣 分享 Led Zeppelin 的歌曲《Stairway To Heaven》 《Stairway To Heaven》- Led Z...

小小编辑
今天
1
0
node调用dll

先安装python2.7 安装node-gyp cnpm install node-gyp -g 新建一个Electron-vue项目(案例用Electron-vue) vue init simulatedgreg/electron-vue my-project 安装electron-rebuild cnpm ins......

Chason-洪
今天
3
0
scala学习(一)

学习Spark之前需要学习Scala。 参考学习的书籍:快学Scala

柠檬果过
今天
3
0
通俗易懂解释网络工程中的技术,如STP,HSRP等

导读 在面试时,比如被问到HSRP的主备切换时间时多久,STP几个状态的停留时间,自己知道有这些东西,但在工作中不会经常用到,就老是记不住,觉得可能还是自己基础不够牢固,知识掌握不够全面...

问题终结者
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部