跳至主要内容

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

评论

此博客中的热门博文

AutoHotKey 新手入门教程

AutoHotKey 真是一个好玩的工具!短短几行代码就是先了“窗口置顶”、“窗口透明”等功能,之前我还特意为此装了好几个小工具,现在都可以卸掉了。闲来无事,就把 Quick Start 翻译了一下,我没有逐字逐句地翻译,有时候我嫌原文罗嗦就用自己的话概括地描述了一下。 原文地址:http://www.autohotkey.com/docs/Tutorial.htm 教程目录 创建脚本 启动程序 模拟鼠标键盘 操纵窗口 输入 变量与剪切板 循环 操纵文件 其他特性 创建脚本 每个脚本都是一个纯文本文件,由一些能被 AutoHotKey.exe 执行的命令组成。一个脚本可能还包含 热键 和 热字符串 。如果没有热键和热字符串,脚本在启动的时候就会从头依次执行到尾。 创建一个新的脚本: 下载 并安装 AutoHotkey。 右击鼠标,选择 新建 -> 文本文档 。 输入文件名并确保以 .ahk 结尾。例如:Test.ahk。 右击文件,选择 编辑脚本 。 输入以下内容:#space::Run www.google.com 上一行的第一个字符 "#" 代表键盘上的 Windows 键;所以 #space 表示在按住 Windows 键后再按空格键。"::" 后面的命令会在热键激活后执行,在本例中则会打开谷歌主页。继续按下面步骤操作,来执行这个脚本: 保存并关闭该文件。 双击该文件来启动它。在系统托盘里会出现一个新图标。 按下 Windows 和空格键,网页会在默认的浏览器里打开。 右击系统托盘里的绿色图标可以退出或编辑当前脚本。 注意: 可以同时启动多个脚本,并且在系统托盘里都会有一个相应的图标。 每个脚本都能定义多个 热键 和 热字符串 。 想让某个脚本开机即启动,可以将它的 快捷方式放到开始菜单的启动目录里 。 启动程序 命令 Run 可以运行程序、打开文档、网页链接或快捷键。请参看以下示例: Run Notepad Run C:\My Documents\Address List.doc Run C:\My Documents\My Shortcut.lnk Run www.yahoo.com Run mailto:someone@somedoma...

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 共享。