2. 开放封闭原则
Software entity should be open for extension,but closed for modification
依据这个原则,我们在设计的时候,就要考虑设计中什么会发生变化,并且不让这变化影响到设计,当然在这里就会遇到“过度设计”的问题了,而且预测变化对经验的要求比较高,创建抽象所付出的代价也很大,所以只能限定在可能的变化上,仅仅对程序中呈现出频繁变化的部分进行抽象。
我们至少应当做到的是:一种可变性不应当散落在代码的很多角落里,而应当被封装在一个对象里面,更不应该和另外一种可变性混在一起。
3 Liskov替换原则
子类必须能够替换掉它们的父类。
另外还有一句话就是:子类具有扩展父类的责任,而不是重写的责任。
这个原则在Thinking in Java(the 2nd editon)和Effective Java中均有提及,当时想到的是因为父类是一组子类公共特征的抽象,所以子类理所当然应该可以替换掉自己的父类。后来看了《敏捷软件开发》中对于这个原则的阐释,才明白这里所说的抽象,其真正意义应该是对对象行为方式的抽象。也就是说,IS-A关系是就行为方式而言的,即使是在现实生活中看上去具有IS-A关系的两个对象,如果在程序中它们的行为方式不相同,那么就不应当具有IS-A关系。
比如说,正方形和矩形,按照数学概念,正方形应该属于矩形——Square is a Rectangle。但是,如果在程序中有一个方法,只改变矩形的长,但是不改变矩形的宽,这时候正方形和矩形的行为方式就不相同了,所以在这种情况下,Square isn’t a Rectangle。
获取系统行为的基础是用例分析(详见《UML和模式应用》),各个用例之间的交互过程就是系统行为的触发过程。我们通过用例契约来定义用例实现的目标,描述一个操作执行前后系统的状态变化,并声明该操作的前置条件和后置条件,这样就可以清楚地得到系统行为。
上面说的是分析阶段的工作,同样在设计阶段,我们也应该为每一个类制定契约,定义它的行为方式,声明每一个方法的前置条件和后置条件,从而得到对象的行为方式。