跳至主要内容

《HFDP》读书笔录(六)——依赖倒置原则

六、依赖倒置原则(Dependency Inversion Principle

依赖抽象,不依赖具体类。

扯淡:其实我有时想,也许类继承上这种树状结构,理想状态会是仅仅有叶子节点是具体类(所有具体类都是叶子节点,叶子节点可能不是具体类)。

这个原则说明了:不能让高层组件依赖低层组件,而且,不管高层或低层组件,“两者”都应该依赖抽象。

所谓高层组件,是由其他低层组件定义其行为的类。例如上例中的借阅管理系统,它的行为是由可借阅的对象决定的,而可借阅的接口则是一个低层组件。

其实,这个原则听起来很像是“针对接口编程,不针对实现编程”,不是吗?的确很相似,然而这里更强调“抽象”。

另外,我觉得“针对接口编程”是从实现者被调用者)的角度来看“接口”,而“依赖倒置原则”是从调用者的角度来看:

OO是符合人的思维的,人在某一个时刻理解某一个对象,往往只是看他们的某个侧面,而并不是他们的全部。

假设,我在公司里组织了一批Java程序员,我和这些人在公司里交流是通过“Java程序员”这个统一的接口,而“Java程序员”可能并非他们的全部。

模拟到代码里,就是这么人的类"class Person extends ... implements Java程序员 ..."

而在我和他们交互时,仅仅使用了"Java程序员 一号员工 = [张三];",这是比较符合生活逻辑的。他们各自在家庭里可能有充当了“儿子”、“女儿”等不同的身份。而我们在公司这个环境下,只关心他们是“Java程序员”这一部分的特性,这才是合理的。

因此,直接用"Java程序员 一号员工 = new Person("张三");",这样针对实现来写,就太过具体,让我们关心的事情太多了,有很多冗余。不符合此原则。

于是引入“工厂模式”等“创建型模式”就是为了避免违背这一原则。

下面的指导方针,能帮你避免在OO设计中违反依赖倒置原则:

  • 变量不一可以持有具体类的引用。
      如果使用new,就会持有具体类的引用。你可以改用工厂来避开这样的做法。

  • 不要让类派生自具体类。
      如果派生自具体类,你就会依赖具体类。请派生自一个抽象(接口或抽象类)。

  • 不要覆盖基类中已实现的方法。
      如果覆盖基类已实现的方法,那么你的基类就不是一个真正适合被继承的抽象。基类中已实现的方法,应该由所有的子类共享。

正如同我们的许多原则一样,应该尽量达到这个原则,而不是随时都要遵循这个原则。我们都很清楚,任何Java程序都有违反这些指导方针的地方!

但是,如果你深入体会这些方针,将这些方针内化成你思考的一部分,那么在设计时,你将知道何时有足够的理由违反这样的原则。比方说,如果有一个不像是会改变的类,那么在代码中直接实例化具体类也就没什么大碍。我们平时不是在程序中不假思索地就实例化字符串对象吗?就没有违反这个原则?当然有!可以这么做吗?可以!为什么?因为字符串不可能改变。

另一方面,如果有个类可能改变,你可以采用一些好技巧(例如工厂方法)来封装改变。

评论

此博客中的热门博文

JavaScript中的字符串乘法

JavaScript中的字符串乘法 redraiment, Date 原文 原文地址: http://www.davidflanagan.com/2009/08/string-multipli.html 原作者:David Flanagan In Ruby, the "*" operator used with a string on the left and a number on the right does string repetition. "Ruby"*2 evaluates to "RubyRuby", for example. This is only occasionally useful (when creating lines of hyphens for ASCII tables, for example) but it seems kind of neat. And it sure beats having to write a loop and concatenate n copies of a string one at a time--that just seems really inefficient. I just realized that there is a clever way to implement string multiplication in JavaScript: String.prototype.times = function(n) {     return Array.prototype.join.call({length:n+1}, this); }; "js".times(5) // => "jsjsjsjsjs" This method takes advantage of the behavior of the  Array.join()  method for arrays that have undefined elements. But it doesn't even bother creating an array with n+1 undefined ele...

DAO层测试

<dependency> <groupId>com.wix</groupId> <artifactId>wix-embedded-mysql</artifactId> <version>2.1.4</version> <scope>test</scope> </dependency> 利用 wix-embedded-mysql 把MySQL嵌入到进程中,作为内存型的MySQL来做单元测试。 脚本: resources/migrations/mysql/<database>/<timestamp>_<action>.sql 但多个项目需要共享数据库脚本,可能可以用 git submodule 共享。

人所不欲,勿施于人

谁说博客也要像论文一样结构清晰、有条理?! 软件卸载 昨天整理自己的本本,卸载了 VMware 7.0 + 深度XP,MS Office 2007 以及 Visual Basic 6.0。我承认这些都是盗版软件,不过剩下的应用程序都是自由软件(freeware)或免费软件(freeware),这下我的计算机“干净”了。闲来无事,我就细数了一下当初装这些软件的原因: VMware + XP:当初刚买本本的时候,正好在上软件工程实践,紧遵老师的教导“将自己的开发环境随身携带”,自然第一款软件就是装了虚拟机(学校机房里是肆无忌惮地用盗版 VMware),另外上课指定使用 Visio 作图,那也只好一起装了;当然,也有部分原因是因为某些人的计算机装的是 XP,我这边有个 XP 环境也是为了方便问题重现(我的本本预装了 Vista)。 MS Office 2007:在毕设期间,我也还是用 Open Office 和 WPS 2010,但现在公司用的却是 Office 2007(正版)。我这次卸载这款办公软件其实也是在提醒自己:工作的事情要在工作时间里完成! VB 6:你可能无法想象在我们科班的毕业设计中有多少是 VB6 项目,从大二开始,每逢毕业将至,总会有人来找我帮忙看那些不晓得从哪儿搜罗来的 VB6 代码,经不住软磨硬泡,我总会帮着改改;另一个原因在我自己,我一直下不了决心去学 MFC 等,所以但凡要做 GUI 程序,我都是拿 VB6 来画界面,再调用由 C 语言开发的 DLL 库,不过现在改用 QT,于是 VB6 可以功成身退了。 己所不欲,勿施于人 有些人就喜欢把自己的事全盘交托给别人来做,我一直不明白他们既然有精力去说服别人,为什么就没耐心自己去完成(所以我下面说人和人之间是无法理解的)。既然自己都认为这是无聊的事情,为什么偏偏又假设其他人会愿意无偿地帮你来完成呢? 两千多年前,孔老夫子提出“己所不欲,勿施于人”的观点,但到了今天,我听到关于这句话时的语境普遍是,A说:“那个XX东西你也不要了(或要了也没用),不如就让给我吧?”,B就义正言辞地反对:“那怎么可以!己所不欲,勿施于人嘛。” 己所甚欲,勿施于人 易中天老师在《百家讲坛》讲解诸子百家...