1 类型一致原则:如果S是T的一个子类型,那么S必须与T保持一致。如果在任何需要类型T的对象出现、且在任何存取方法执行时都需要保持正确性的场合,都能够使用类型S的对象,那么就可以说S和T是同一类型的。
例如,圆是椭圆的一个子类型,那么圆类型的对象同时也是椭圆类型的对象,任何可以将椭圆类型的对象作为参数的方法,都应该可以接受圆类型的对象作为参数。
在一个良好的面向对象设计中,类/子类的层次结构应该是和类型/子类型的层次结构保持一致的。也就是说,应当把每个子类设计成每个父类的子类型,同时遵守类型一致的原则。
为了保证子类型的一致性,首先需要保证子类的不变式的要求比父类的不变式的更加严格或者至少同样的严格。比如说Rectangle类具有不变式w1 = w2 and h1 = h2,那么Square类作为Rectangle类的子类,它的不变式w1 = w2 and h1 = h2 and w1 = h1就比父类的不变式要求更加严格。将这个要求分解开来,也就是要求以下三个约束条件:
1)
父类的每个方法都出现在子类当中,并且拥有相同的方法名、签名;
2) 子类中的方法的前置条件的要求应当比父类中对应方法的要求宽松或者相同(逆变量原则);
3) 子类中的方法的后续条件的要求应当比父类中对应方法的要求更严格或者相同(协变量原则)。
2 闭合行为原则:在基于类型/子类型的继承层次中,当执行任何方法(getter方法和setter方法)时,每个类的行为(包括继承的父类的方法)都应当遵守类的不变式。
比如,Triangle是Polygon的子类,Polygon有如下两个方法:move和addVertex。对于一个Triangle的对象tri,作用move方法,将使得tri的位置发生变化。Tri在移动之后仍然保持为一个三角形(也就是保持为Triangle类型)。Polygon的行为将对象留在了同Triangle的状态空间一样的状态空间之中,这种情况下,可以说子类Triangle在父类的方法move所定义的行为下是闭合的。但是addVertex方法所定义的行为会导致tri增加一个顶点,使得tri不再是Triangle类型的对象而成为了一个四边形。这种情况下,可以说子类Triangle在父类的方法addVertex所定义的行为下不是闭合的。
通常,父类行为下的子类的闭包不会自动产生,程序员应当在设计子类的时候对此加以考虑,对于父类中会违法子类不变式的方法加以覆盖。