软件设计漫谈之三:30分钟掌握面向对象类的设计原则

                            30分钟掌握面向对象类的设计原则

看过设计模式的人不少,但看过Martin的面向对象的设计原则的估计不多(详情可参考《敏捷软件开发:原则、模式与实践》)。实际上这两者是相辅相成的:设计模式是具体的实践方法,而设计原则是指导思想;设计模式让你知道How,而设计原则让你知道Why

《敏捷软件开发:原则、模式与实践》原著洋洋洒洒几十万言,介绍面向对象类的类的设计几个原则也有几十页,没有耐心的朋友估计看不下去。没关系,这里我给大家一个精简版的,让你读完本博就能够初步掌握这些原则,而且附送一些疑难解答,让你更加容易理解。

1                 SRP(单一职责原则)

这个原则看起来很简单,就是说一个类只能承担一个职责

但这里有一个关键:“职责”是如何理解的?

按照汉语的理解,职责其实分为两部分:“职”和“责”。“职”就是这个类是什么,而“责”就是这个类要干什么

举个例子来说:Door是一个对象,那么它的“职”就是门,“责”就是“开门、关门”等;而Lock的“职”就是锁,“责”就是“上锁、开锁”。如果设计的时候Door同时具有锁的职责,那么Door就违反了SRP原则。

2                 OCP(开闭原则)

相信这是大家见得最多的原则,而且很多人都是这么解释的“对扩展开放、对修改封闭”,更加有人总结为“不修改代码增加新的功能”!

太神奇了,不修改代码增加新的功能!但我不免疑惑:不修改代码怎么增加新的功能呢?难道代码会像生物一样,基因变异?

仔细研究过后才发现,原来是这些总结的人误导了我,根本不是什么“不修改代码增加新的功能”,也不是那个省略了主语的“对扩展开放,对修改封闭”,而是“被调用者开放扩展,调用者封闭修改

还是举门的例子:Door对象是被其它对象例如人People调用,那么Door就是被调用者,People就是调用者。Door对象可以扩展为“防盗门”、“防火门”、“逃生门”等,但People在调用的时候不需要关注具体是什么门,只需要调用这些门公共的“开门、关门”等操作即可。

3                 LSP Liskov替换原则)

这个看起来是比较难理解的原则,但我可以给一个很容易理解的总结:“子类的输入不能比父类多,子类的输出不能比父类少!”。

“输入”就是指调用类的时候要给出的条件,最常见的就是函数参数,而一般的语言都可以从语法上保证这种子类和父类相同函数的参数必须相同;还有另外一种隐性的条件即“假设”或者“要求”也必须相同。

举个简单的例子:长方形和正方形。按照数学的定义,正方形是特殊的长方形。依照此定义看起来好像可以将正方形定义为长方形的子类,但实际上在面向对象设计中则是不行的,因为按照设定长方形长宽的方法不能来设定正方形的边长,正方形要求长宽必须相等,而长方形没有此“要求”。这就是子类的“假设”或者“假设”多于父类,违反了LSP原则。

“输出”就是指调用类后返回的结果。即:子类的返回结果要包含父类的返回结果,可以多但绝对不能少。

为什么设计时要满足LSP原则呢?其实很简单,因为调用者看到的只有父类,它根本不知道到底是哪个子类,调用者所有的处理都是基于父类提供给调用者的信息(包括输入信息和输出信息)。

4                 DIP(依赖反转原则)

这个原则看起来有点吓人,换个简单的说法你就明白了,其实它就对应于《设计模式》开头提到的两个原则中的一个:“基于接口编程,而不是基于实现编程”。(另外一个是什么呢?)

这里的“编程”包括“调用者”和“被调用者”的编程。“调用者”基于接口进行调用,“被调用者”基于接口进行具体实现。

注意:和“依赖反转”类似的两个比较容易混淆的概念是“依赖注入”和“控制反转”,详细可以参考如下博文:http://blog.csdn.net/taijianyu/archive/2008/04/28/2338311.aspx

5                 ISP(接口隔离原则)

这个原则也很简单,就是说一个对象不要提供多个接口,不同的接口应该分离到不同的对象上去。

虽然大师的水平不容怀疑,但这里我还是要怀疑一下:SRPISP本质上应该是一个原则,只是说法不一样而已

为什么这么说呢?大家可以看看SRP原则,它说的是“单一职责”。那么职责最终体现在对象上是什么呢?对,不就是接口么!也就是说,如果遵循了SRP原则,接口自然就隔离了。

 

================================================ 

怎么样,看到这里是否只花了30分钟?那么这些原则你是否基本都掌握了呢?如果还有什么疑问,欢迎留言讨论。

 

上一篇:揭开this的神秘面纱-属性篇 | 带你学《Java面向对象编程》之七


下一篇:pytest中setup与teardown方法与类级别的实现