软件方法(下)第8章分析之分析类图—知识篇Part13-警惕拼凑泛化
8.3.2.3 Liskov替换原则
只是从名称上来判断,并不能成为泛化关系的最终证据。1988年,Liskov在“Data abstraction and hierarchy”文章中提出了一个判断的标准,后来被称为Liskov替换原则(LSP):
如果对于每个类型S的对象O1,都有类型T的对象O2,对于所有以T的形式定义的程序P,当O1被O2替换时,P的行为不变,那么S是T的子类型。
很多书和文章中提到Liskov替换原则时,会以矩形和正方形(有时会换成椭圆和圆)的问题为例。
假设把正方形看作矩形的子类,如图8-107。
图8-107 把正方形当作矩形的子类
设置某矩形的A边长为4,再设置B边长为5,按照设想,此时求面积应该得到4×5=20。如果用正方形代替矩形,要么为了保持正方形的约束,最终得到的面积是5×5=25,要么两边自由变化,正方形就不再是正方形了。
根据Liskov替换原则可以判断出图8-107不合适,但Liskov替换原则没有解释其中的原因。
Bertrand Meyer在“Object-Oriented Software Construction”一书用契约的观点解释:子类操作的前置条件应该不强于超类,后置条件应该不弱于超类。
例如,构造一个矩形对象需要提供两个边长参数,对这两个参数并无要求,而构造正方形对象却要求这两个参数必须相等,即,子类操作的前置条件强于超类,不合适。
我们仅从属性的角度来看看。如果独立描述矩形和正方形所需的属性,可以得到图8-108。
图8-108 建模矩形和正方形的属性
从图8-108可以得知,正方形的属性比矩形还少一个,把正方形作为矩形的子类是不合适的。
反过来,矩形倒是更像正方形的子类,复用边长,再加一个边长。Bertrand Meyer在“Object-Oriented Software Construction”中就提到,某类库的早期版本,就是让矩形继承正方形。
图8-109 摘自“Object-Oriented Software Construction( Second Edition )”, Bertrand Meyer, 1997
要建立正确的关系,可以减弱超类的定义,由子类定义有几个边长属性,如图8-110。
图8-110 超类不定义有几个边长属性
图8-110中,正方形是矩形的子类,但不是自由矩形的子类。自由矩形、正方形和黄金分割矩形(边长比为黄金分割比0.618····:1)等是互相不重叠的矩形子集(子类)。
也可以把A边长和B边长属性保留在矩形中,正方形、黄金分割矩形如果适用特殊的规则,可以作为矩形的状态。正方形、黄金分割矩形等没有增加新的属性,只是要求A边长和B边长的值符合某个约束,也就是说,正方形、黄金分割矩形是属性值组合中的一个子集的表征,这个就是状态。如图8-111。
图8-111 用状态来表达不同矩形
8.3.2.4 警惕拼凑泛化
您可能注意到,以上我们尽量通过属性(包括关联)来解释泛化关系。
虽然泛化带来的好处如Liskov所说,是落在行为上——“P的行为不变”,但如果抛开属性直扑行为,很可能会带来“假泛化”、“假面向对象”。
如图8-112,因为X和Y都有操作op1,所以泛化出A,把op1提上去成为抽象操作。这个没有问题。问题出在前面,怎么知道op1作为X和Y的操作是合适的?最终的依据还是X和Y的属性(包括关联)。
图8-112 操作怎么来的,需要有依据
有一种偷懒遮羞布,就是胡乱安排操作,然后拼凑出泛化关系,根本不顾安排的操作是否合理。
一些“面向对象”实践经常得到各种名称最后有or或er的类(汉语则为“器”),就有这种嫌疑。开发人员可能一开始按照面向过程的思路噼里啪啦把代码写出来,然后出于赶时髦需要“面向对象”,他就把过程名称加上or或er作为类名称,然后把原来的过程作为or或er类的操作。
当存在多个类似过程时,还可以加上一些泛化关系(或接口-实现)来做点缀,如图8-113,这样看起来就更有“面向对象”的味道了。
图8-113 带有泛化关系的or或er类
可能在某些开发人员眼里,图8-113很有格调,可以用来吹嘘的高大上词汇有:OCP(开放-关闭原则)、DIP(依赖倒置原则)、模板方法模式……等。这些由泛化关系衍生出来,被网红圈子广泛吹嘘的原则和模式作用十分有限,指望了解被包装出来的“SOLID原则”之类就能应对软件复杂性,那真是太天真了。
*很多人可能是从Robert C. Martin的书《敏捷软件开发-原则、方法与实践》中了解到OCP、DIP、LSP等。这些原则并非Robert C. Martin首先提出,而且和敏捷过程没有必然关系,但Robert C. Martin起这样的书名,经常会导致开发人员误解这些是敏捷人士提出来的。*
or或er类往往没有属性,只有操作,大量的逻辑仍然隐藏在子类操作的实现中。当然,开发人员也可能觉得这是好事,“这说明我有算法啊!”——“算法”,又是一个可以吹嘘的高大上词汇。
和其他的偷懒遮羞布类似,这种做法一一对应,思考工作量小,还有各种高大上词汇护法,于是开发人员洋洋得意,感觉自己已经很厉害了,连称“受用”,纷纷去拥抱这种偷懒遮羞布。
or或er类有时会使用“策略模式”作为伪装,如图8-114,哇,我可以灵活组装各种策略!顺便再吹一通“组合优于继承”之类,其实还是换汤不换药,逻辑仍然隐藏在子类操作的实现中。
图8-114 “策略模式”换汤不换药
如果一个类的命名中有类似这些内容:er、or、器、策略、Strategy、Policy、规则、Rule、算法、Algorithm……然后这个类或其子类的操作中有长长的“算法”,那么应该思考一下,长长的“算法”中到底定义了哪些变量?哪个部分的代码最复杂?
背后往往就是候选的实体类以及需要封装的操作,尽量分离出实体类,把各种逻辑尽量封装在实体类的操作中。这样的思考更辛苦,但也更有价值。
[推荐升级]23套UML+EA和StarUML的建模示范视频-全程字幕(2022.6.1更新)
《软件方法》书中自测题-题目全文+分卷自测(1-8章)16套111题
相关文章
- 网页翻译最轻量级方法【微软】翻译书签!!!
- 管家婆软件【提成管理】使用方法
- 简单的java图片存储方法
- Seurat软件学习6-多模型参考映射的方法
- 组织结构的建模-软件方法(下)第9章分析类图案例篇Part06-案例二-智能建模工具
- 软件方法(下)第9章分析类图案例篇Part05-案例二-智能建模工具
- 软件方法(下)第9章分析类图案例篇Part03
- 《软件方法》随想(1)一本让我看完发呆半个小时的书
- 《软件方法》随想(2)业务建模之愿景
- 软件方法(下)第8章分析之分析类图—知识篇Part11-类之间的关系
- 伪创新为什么受欢迎-《软件方法》节选
- 利用MDK软件生成bin文件的简单方法
- Acrobat DC软件安装教程方法,PDF编辑器全版本软件下载
- Adobe Acrobat Pro DC 2018(PDF) 软件下载安装包教程(附下载方法)
- PR2019软件下载及安装方法!--Premiere全版本软件下载
- pdf批量去水印的方法,其实这个软件你天天用……pdf编辑器全版本下载
- CSS 实现水平和垂直居中的三种方法
- 【python实操】安装软件时候选择文件安装目录模块的实现方法
- 便携式钻孔测斜仪数据采集仪的使用方法
- 「三维CAD软件」SolidWorks2020下载安装教程及激活方法
- SQL Server 利用触发器对多表视图进行更新的实现方法
- linux安装软件时提示软件包不存在的解决方法
- JSP Out.println()方法:输出空字符串和换行符号
- 安装软件Linux环境下安装程序的编译安装方法(linux如何编译)
- Linux系统快捷关闭软件方法(linux关闭软件)
- Linux下反汇编工具及使用方法(linux反汇编软件)
- Linux 命令行脚本:一种提高效率的新方法(linux命令行脚本)
- PHP从Linux中读取文件的方法(php读取linux文件)
- Linux下安装软件的方法详解(linux安装软件的方式)
- Linux下轻松删除软件的方法(linux 如何删除软件)
- 解决Oracle数据库中获取余数的方法(oracle中获取余数)
- 值Oracle中计算最大最小值的方法(oracle中最大最小)
- 解决Oracle数据库编码问题的方法(oracle中怎么改编码)
- form.submit()不能提交表单的错误原因及解决方法
- PHP获取表单所有复选框的值的方法