面向对象设计之四 类型一致性和闭合行为原则

类型一致性和闭合行为

 

1 类class和类型type

    从抽象的角度来理解,最好将类视为类型的实现。也就是说类型包括了类的目标以及类的状态空间和行为。实际上,一个类型可以有多个类,每个类都包括自己独立的内部设计。一个典型的例子是STL链表类型,在GNU实现中的list类与windows实现的list类,二者拥有相同的外部特性,但在内部实现上却有着不同。

    注意子类型与子类的区别,在下一节将给出子类型的定义。从语法上讲,我们可以将任意一个类定义为另一类的子类,但从语义上讲,它们没有任何意义,因为这些子类和其相应的父类之间没有任何关系。因此,即使s是t的子类(派生类),并不意味着s是t的子类型。

 

2 类型一致原则

    类型一致性原则来源于抽象数据类型原理,此原理是面向对象的基础。类型一致性原则对于创建类库的类层次结构至关重要,在某些书中也叫为liskov替换原则,此原则的表达为:如果s为t的真子类型,则s必须与t一致,即类型s的对象可以出现在类型t的对象所需要的任何环境中,并且当此对象的任何获取操作执行时,仍能保证其正确性。

    在完善的面向对象的设计中,每个类的类型必须与其基类相一致,即类或派生类的继承层次结构必须遵循类型一致原则。要这个做的原因在于,为了毫不费力的利用多态性,我们必须能传递子类对象以代替基类对象。

2.1 抗变性与协变性原则

    为了保证子类的类型与基类保持一致,首先要保证子类的不变式至少和基类的不变式一样强。

    其次还需要满足下列三个操作限制条件:

    a 每个基类的操作必须与其子类中一个操作相对应,它们具有相同的名字和函数原型。

    b 抗变原则:每个子类操作的前置条件弱于或等于基类操作的前置条件

    c 协变原则:每个子类操作的后置条件强于或等于基类操作的后置条件

2.2 抗变性和协变性的实例

    雇员类的一个派生类是经理类,但如何才能保证经理类是雇员的真子类呢?

    假设雇员的不变式是gradLevel>0,而经理的不变式为gradLevel>20,这就保证了经理类的不变式比雇员类的不变式强,从而满足了要求。

    其次,两个类都需要计算红利,基类和派生类在计算时只是输入参数范围不同。如果基类的输入参数为[0,5],输出参数为[0,10],则派生类经理的计算操作的前置条件要更弱,即输入参数若比(0,5)更小是不合法的。而后置条件应该更强,应该在(0,10)之间。


 3 闭合行为原则

    在类型一致的情况下,仅在只读情况下即仅执行获取操作时,才能得到完善的设计。如果处理的情况中执行了修改操作,我们还需要遵守闭合行为原则。此原则要求,子类从基类继承的行为必须符合子类的不变式。

    在基于类型/子类型层次结构的继承层次结构中,派生类C的任何操作,包括从基类继承的操作,均应该满足C的类不变式。

    下面举个例子来说明对这个原则的理解。基类为多边形,派生类为四边形。多边形的操作中有一个是move,即将图形整个移动到另一个位置。这个操作对四边形而言,移动之后还是四边形,因此这个操作是闭合的。而另一个操作是add_vertex,即为多边形增加一个顶点。如果四边形执行这个操作,则其不再是四边形了。因此add_vertex操作并不是闭合的。

    从上面的例子中我们看到,基类行为中的子类闭合并不是自动产生的,我们必须将其设计进来。作为派生类的设计者,必须深思熟虑,并明确覆盖其基类的操作,否则将违背派生类的不变式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值