【设计模式】之六大原则(二)
目录
四、Barbara Liskov Priciple (里氏代换原则)
四、Barbara Liskov Priciple (里氏代换原则) ——Liskov Substitution Principle
1、问题由来:
A;“面向对象中一个是鸟类,一个是企鹅类,如果鸟可以飞,企鹅不会飞,那么企鹅
还是鸟吗?企鹅可以继承这个类吗?”
B:“企鹅是一种特殊的鸟,尽管不会飞,但是他也是鸟,当然可以继承。”
A:“哈,你上当了,我说的是面向对象上的设计中,在面向对象设计中子类拥有父类
所有非Private的行为和属性。鸟会飞,二企鹅不会飞,尽管在生物学中,企鹅是鸟,但
是编程世界中,企鹅是不能一父类——鸟的身份出现,所以企鹅是不能继承鸟类的。”
2、定义:里氏代换原则就是一个软件实体中如果使用的是一个父类的话,那么一定适
用其子类,而且它觉察不出父类对象和子类对象的区别。也就是说,在软件里面,把父
类都替换成他的子类,程序的行为没有变化。
3、理解:里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类
原有的功能。它包含以下4层含义:
(1) 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
(2)子类中可以增加自己特有的方法。
(3)当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法
的输入参数更宽松。
(4)当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父
类更严格。
看上去很不可思议,因为我们会发现在自己编程中常常会违反里氏替换原则,程序照
样跑的好好的。所以大家都会产生这样的疑问,假如我非要不遵循里氏替换原则会有什
么后果?后果就是:你写的代码出问题的几率将会大大增加。
五、迪米特原则(Low Of Demeter)
1、定义:一个对象应该对其他对象保持最少的了解。
2、问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另
一个类的影响也越大。
3、解决方案:尽量降低类与类之间的耦合。
自从我们接触编程、设计模式开始,就知道了软件编程的总的原则:低耦合,高内
聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才
能提高代码的复用率。低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那
正是迪米特法则要去完成的。
4、迪米特法则又叫最少知道原则,最早是在1987年由美国Northeastern University
的Ian Holland提出。通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是
说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对
外除了提供的public方法,不对外泄漏任何信息。迪米特法则还有一个更简单的定义:
只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有
耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合
的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方
法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是
说,陌生的类最好不要作为局部变量的形式出现在类的内部。
小结:迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依
赖,因此的确可以降低耦合关系。但是凡事都有度,虽然可以避免与非直接的类通信,
但是要通信,必然会通过一个“中介”来发生联系,例如本例中,总公司就是通过分公
司这个“中介”来与分公司的员工发生联系的。过分的使用迪米特原则,会产生大量这
样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做
到结构清晰,又要高内聚低耦合。
六、合成/聚合复用原则(Composite/Aggregate Reuse Principle CARP)
组合/聚合复用原则(Composite/Aggregate Reuse Principle CARP).组合和聚合都
是对象建模中关联(Association)关系的一种.聚合表示整体与部分的关系,表示“含
有”,整体由部分组合而成,部分可以脱离整体作为一个独立的个体存在。组合则是一
种更强的聚合,部分组成整体,而且不可分割,部分不能脱离整体而单独存在。在合成
关系中,部分和整体的生命周期一样,组合的新的对象完全支配其组成部分,包括他们
的创建和销毁。一个合成关系中成分对象是不能与另外一个合成关系共享。
组合/聚合和继承是实现复用的两个基本途径。合成复用原则是指尽量使用合成/聚
合,而不是使用继承。
只有当以下的条件全部被满足时,才应当使用继承关系。
1. 子类是超类的一个特殊种类,而不是超类的一个角色,也就是区分“Has-
A”和“Is-A”.只有“Is-A”关系才符合继承关系,“Has-A”关系应当使用聚合来描
述。
2 .永远不会出现需要将子类换成另外一个类的子类的情况。如果不能肯定将来是否会
变成另外一个子类的话,就不要使用继承。
3 .子类具有扩展超类的责任,而不是具有置换掉或注销掉超类的责任。如果一个子类
需要大量的置换掉超类的行为,那么这个类就不应该是这个超类的子类。
总结:
说到这里,再回想一下前面说的6项原则,恰恰是告诉我们用抽象构建框架,用实现
扩展细节的注意事项而已:单一职责原则告诉我们实现类要职责单一;里氏替换原则告
诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;接口隔离原则告诉
我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合。而开闭原则是总
纲,他告诉我们要对扩展开放,对修改关闭。
最后说明一下如何去遵守这六个原则。对这六个原则的遵守并不是是和否的问题,而
是多和少的问题,也就是说,我们一般不会说有没有遵守,而是说遵守程度的多少。任
何事都是过犹不及,设计模式的六个设计原则也是一样,制定这六个原则的目的并不是
要我们刻板的遵守他们,而需要根据实际情况灵活运用。对他们的遵守程度只要在一个
合理的范围内,就算是良好的设计。