《软件方法(下)》8.3.3.2 矩形-正方形问题(202405更新)

8.3 建模步骤C-2 识别类的关系

8.3.3 泛化的一些重点讨论

8.3.3.2 Liskov替换原则和矩形-正方形问题

1988年,Liskov在“Data abstraction and hierarchy”文章中提出了一个判断子类型的标准,后来被称为Liskov替换原则(LSP):

如果对于每个类型S的对象o1,都有类型T的对象o2,对于所有以T的形式定义的程序P,当用o1替换o2时,P的行为不变,那么S是T的子类型。

图片

图8-105 Liskov文章截图

很多书和文章中提到Liskov替换原则时,会以矩形和正方形(有时会换成椭圆和圆)的问题为例。

假设把正方形看作矩形的子类,如图8-106。

图片

图8-106 正方形作为矩形的子类

设置某矩形的A边长为4,再设置B边长为5,按照设想,此时求面积应该得到4×5=20。如果用正方形代替矩形,经过上面两次设置后,最终得到的面积是5×5=25。根据Liskov替换原则判断,图8-106不合适。

关于这个问题,网络上搜索到的文章大多是从实现技巧的角度来解释和解决。

而从面向对象思想和领域逻辑的角度来思考和讨论这个问题,上世纪90年代已经有不少文献,讨论得较详细的有:

Bertrand Meyer的书“Object-Oriented Software Construction (2nd Edition)”(1998)

Kazimir Majorinc的论文“Elipse-Circle Dilemma and Inverse Inheritance”(1998)

Walter L. Hiirsch的论文“Should Superclasses be Abstract?”(ECOOP '94)

UML三友之一James Rumbaugh的书“Object-Oriented Modeling and Design”(1991)、“OMT Insights”(1996)

本书从领域知识和集合的角度来谈一谈这个问题。

从领域知识上看,矩形的定义是:有一个角是直角的平行四边形。由此衍生的性质有:对边平行且相等、对角线互相平分且相等、面积=长×宽……。正方形也确实符合矩形的定义并具有矩形的性质,所以,正方形是矩形的子类(子集),是正确的。

但是,矩形还有其他没有画出来的子类(子集),而我们不知不觉地把没画出来的矩形子集的性质当成了所有矩形的性质,导致了冲突。

如图8-107,A1是A的子类,因为A1有公认的名字(例如正方形),所以被显式画出来。但A集合除了A1子集之外,还有其他子集,可能没有想好名字,没画出来。

图片

图8-107 产生错觉的原因

假设此时我们想到一个对象x,x是A集合的一个元素,但不是A1集合的元素。也就是说,x是图8-107中?(最大的?即A-A1)的元素,同时也是A的元素。

如果图上没有显式画出?,只有A1和A,既然x不属于A1,那就只剩下A了。于是,我们在意识中记住了“x是A的元素”,忘记了和A1同一级别的表述应该是“x是?的元素”,然后就会产生?和A相等的错觉。

正如上一小节所说,在建模泛化关系时,没有必要为了完整硬要加一个“其他*”什么之类的,但我们心里必须有这个意识。

合适的处理方法是,把不属于超类的性质从超类移除出去。

可以如图8-108。正方形是矩形的子类,除此之外,还有其他子类,如自由矩形、正方形和黄金分割矩形(边长比为黄金分割比0.618····:1)等。超类不实现“设置A边(a)”、“设置B边(b)”的操作,也不应先入为主地认为操作和属性会一一对应,例如“设置A边(a)”操作只会影响属性“A边长”的值。

图片

图8-108 改进后的矩形泛化类图

更彻底的如图8-109,把属性的定义也放到子类。

图片

图8-109 属性的定义放到子类

还有一个经常容易造成困惑的地方。

一个“自由矩形”对象,一开始的边长是(3,4),设置A边,让边长变成(4,4),它是不是就应该变成一个“正方形”对象?后面还能再自由设置边长吗?

它依然是“自由矩形”,只不过其两个边长在某个时刻碰巧相等,它并不是一个边长为4的“正方形”对象。这两个对象的属性值虽然相同,但遵循的行为规则是不同的。

类不止定义了属性,还定义了和这些属性相关的行为规则。

一把手牌3张K,您觉得这个牌怎么样?

图片

图8-110 扑克牌手牌

如果是“斗地主牌”,算是好牌,如果是“21点牌”,就已经爆了。

还有一种做法,把“等边”、“黄金分割”等都看成状态,如图8-111。

图片

图8-111 用状态来表达不同矩形

如果说一旦变成了正方形就永远只能正方形,可以如图8-112。

图片

图8-112 一旦正方形就只能正方形

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值