天天看点

《软件方法》第8章 分析 之 分析类图(2)

8.1.6 识别分析类和属性的要点

8.1.6.1 关于中英文命名

该用中文就用中文,该用英文就用英文,该用日文就用日文。中英文命名问题和设计工作流(编码、设计数据库……)中碰到的问题是类似的。分析类虽然不包含设计工作流的知识,但它是设计工作流的基础。反对在设计工作流中使用中文命名(类名、属性名、表名、字段名……)的理由可能是编译器不支持、DBMS不支持、切换输入法太麻烦、版式不好看等。编译器、DBMS因素随着时代的发展慢慢地不再是问题,版式、切换输入法问题在画图建模中不存在,所以用中文名称不是大问题。当然,如果开发团队是国际化的,就是另外一种情况。

总之,分析类和属性(包括后面要添加的操作)的名称应该以方便开发团队思考和交流领域知识为首要考虑因素。

EA提供了别名(Alias)。名称可以用英文——其实不是英文,是编译器广泛支持的符号集合。除名称外,再加一个别名用于显示。不过,建议暂时不用。如果熟悉的领域词汇是中文名,在那时还需要花时间去想一个英文名,分散了建模中思考的精力,更不用说模糊的汉语拼音和错误的英文会给后续工作带来的麻烦了。

8.1.6.2 命名中不带冗余信息

不要在类名的最后加"类"字;不要在类名的前后加"Class"或"C";不要在类名的最后加"情况"、"信息"、"记录"、"数据"、"表"、"库"、"单"等词。

《软件方法》第8章 分析 之 分析类图(2)

图8-47 类名后面不需要加冗余词

如果系统关注的焦点是"数据处理",处理的数据是什么内容无所谓,"信息"、"数据"也可以作为一个类的名称,但它和"人员"不属于一个领域,不在一个抽象级别。如图8-47。

《软件方法》第8章 分析 之 分析类图(2)

图8-48 "信息"作为类

当然,如果一个带有以上后缀的词在领域中已经存在很久,成为了领域的术语,直接使用无妨。例如"订单"带有"单"字,实际上描述的是一次"购买",但"订单"已经在领域中广泛使用,可以作为类名。

属性名称前不需要加类名。

如图8-49。按"类的属性"念出来,"人员的姓名"很好,"人员的人员姓名"冗余了。

《软件方法》第8章 分析 之 分析类图(2)

图8-49 属性名称前不需要加类名

英文名用单数。

如图8-50。"顾客"的实例是一名顾客,"顾客们"的实例是什么?一个集合?

《软件方法》第8章 分析 之 分析类图(2)

图8-50 英文名用单数

设计工作流中的数据库表名也应该用单数。有一些开发人员习惯在最后加s(当然,复数未必是加s),甚至有一些框架直接就在表名后面加上s,理由是表里有很多行。这个问题和上面提到的类名称后面加个"类"字是一样的,都是一种冗余。"类"、"表"的概念已经隐含了"多个对象"、"多行"的意思。说"我是一个类,我的名字叫顾客"就够了,不需要说"我是一个类,我的名字叫顾客类"或者"我是一个表,我的名字叫顾客们"。

8.1.6.3 命名要一致

同一个概念要用一致的词汇表达。例如"宝贝"还是"商品"?"顾客"、"客户"还是"会员"?如果没有差别,应该统一到同一个词汇,如果有差别,应该在类图上表达其中差别。如果还没有思考到如何在类图上表达,可以先在类的说明中阐述确切含义。

如图8-51,左侧的几个概念中,"宝贝"和"商品"、"顾客"和"客户"含义相同,把它们合并,同时在类图上更精细地表达"用户"、"顾客"、"会员"概念之间的差别。

《软件方法》第8章 分析 之 分析类图(2)

图8-51 理清混淆的概念

8.1.6.4 切勿照猫画虎

如果用例规约是完整的,从用例规约中找到实体类应该问题不大。不过,有的开发人员根本没有写用例规约,也不具备基本的对象建模技能,会发现自己找不到合适的类甚至找不到类。

类图长得像用例图。

例如,一个电商系统,开发人员一开始心里有一个想法,要做一个“搜索商品”的功能。那么要实现这个功能,需要什么类呢?开发人员干脆就按看到的表面现象来找类,得到的类图长得非常像用例图,如图8-52。

《软件方法》第8章 分析 之 分析类图(2)

图8-52 长得像用例图的类图

显然,系统之所以能够为顾客查询“屏幕尺寸为4.6-5.0英寸的Android手机”,不是因为它记住了哪位顾客查询过哪件商品,而是因为它记住了各种商品的类别和特征。正确的类图应该类似于图8-53。

《软件方法》第8章 分析 之 分析类图(2)

图8-53 更合适的类图

类长得像过程。

没有基本的面向对象抽象思维的开发人员,可能会按照面向过程的思路噼里啪啦把代码写出来,然后就感觉似乎不需要什么类来帮忙了。即使出于赶时髦需要类,他就把过程名称加上or或er作为类名称,然后把原来的过程作为or或er类的操作,有时还用上一些泛化关系来做点缀,如图8-54所示。这样做表面上似乎是面向对象了,实际上是换汤不换药,没有得到面向对象的好处。

《软件方法》第8章 分析 之 分析类图(2)

图8-54 伪面向对象抽象

碰到这种情况,可以思考如果按照面向过程的思路来编码,什么地方的代码最复杂?长长的“算法”中定义的变量,往往就是候选的实体类。

类长得像报表。

在没有用例规约的情况下,开发人员可能是根据手上的一些类似于报表的需求素材来找类。如果缺少抽象能力,就会把报表直接变成类,照抄报表的每一栏作为属性,如图8-55所示。

《软件方法》第8章 分析 之 分析类图(2)

图8-55 长得像报表的类

报表只是视图,把更为本质的多个实体类的一些属性组织在一起。针对这种情况,可以用下文介绍的类和属性检验规则来深入思考背后的领域概念。

正如第一章所说,需求和设计不是一一对应的。设计源于需求,高于需求。

8.1.6.5 使用核心域术语

一个领域之所以能作为“领域”为人认知,必定存在一套日益完善和精确的术语体系。每个术语有其独特的、其他领域术语不能替代的内涵。分析模型中的名称应该来自核心域的术语体系。

建模人员可能会用自己熟悉的领域的术语体系去代替不那么熟悉的核心域术语体系,还引以为豪。例如,面对一段集装箱领域装箱规则的描述,建模人员立即在大脑中把它转换成自己熟悉的概念:栈、链表、树……认为这是“透过现象看本质”,甚至宣称“我就是程序,程序就是我”!

Fred Brooks在《人月神话》[Brooks 1995]中引用了James Coggins的一段话:

The problem is that programmers in O-O have been experimenting in incestuous applications and aiming low in abstraction, instead of high. For example, they have been building classes such as linked-list or set instead of classes such as user-interface or radiation beam or finite-element model.

问题是面向对象程序员在开发错综复杂的应用时,关注的是低层次,而不是高层次的抽象。例如,他们开发了很多像链表或集合这样的类,而不是用户界面、射线束或者有限元模型。

不同领域有不同的难题,因为觉得困难,所以对真正要解决的核心域问题视而不见,却花精力去做那些自己熟悉的、他人已解决的其他领域问题,是一种逃避。

涉众常使用的词汇不一定是合格的领域术语。涉众经常使用一些不严谨的称呼,例如用颜色来表征:绿本(小产权证)、绿卡(永久居民卡)、绿单(预约单)。这些称呼中的信息是不稳定的,如果政府决定改用其他颜色,括号里的概念不变,括号外的称呼就得变化了,即使已经形成了习惯一直沿用下去,真实的内涵和字面的意思已经大相径庭。

这样的现象是正常的。正如第7章所说,涉众关注的是涉众利益,不关注系统需求,更不用说分析了。某类涉众的领域知识可能会很片面,对领域概念认识不深刻。怎么能寄望一个使用陌陌约会的屌丝青年清楚社交六度空间理论呢?

为了精确地使用领域术语,建模人员需要同时精通两方面的知识——领域知识和建模知识,才有可能得到深刻反映领域内涵的分析模型。缺少领域知识的建模好手固然比两方面知识都不具备的小白要好,但得到好模型的最大障碍还是对领域知识的理解不足。

这时,建模人员需要领域专家的帮助。帮助可以是直接的——建模人员和领域专家一起工作;也可以是间接的——建模人员阅读专业书籍。建模人员和领域专家两个身份可以合一,建模人员慢慢具备领域专家的能力,或者领域专家慢慢掌握建模技能。至于哪条融合的路线更好,没有标准的答案,应该视市场更需要 “懂软件技术的医学人士”还是“懂医学技术的软件人士”而定。

8.1.6.6 核心域透镜

为了避免核心域概念被非核心域概念掩盖,我们可以采用一种如图8-56所示的“核心域透镜”的思考方式,从核心域视角去看所有的事情。

《软件方法》第8章 分析 之 分析类图(2)

图8-56 用核心域透镜映射各种概念

例如,以“学习和考试”作为核心域,经过透镜前后的概念对比如图8-57。

原描述 映射后的核心域概念 原描述过去变体 原描述将来变体(猜想)
Powerpoint 演示工具 黑板、玻璃幻灯片、赛璐珞幻灯片 全息
检查IP地址 检查重复听课学生 看脸、看签名

检查新的协议地址

检查大脑芯片标识

点击“开始”按钮 开始考试 观察到考生开始书写
向数据库“答题”表添加一条答题记录 答题 在答卷上涂黑一格

图8-57 经过透镜前后的概念对比

再以上文提炼的UMLChina系统类图,即图8-58为例,被圈住的部分属于“发邮件”领域的概念。使用电子邮件的方式来通知学员举办公开课的信息,只是现在的手段。这个手段是变化的,过去也许是通过电话和传真,将来也许是通过短信、QQ和微信。如果通过“举办公开课”的核心域透镜映射,就可以得到“通知手段”、“通知”、“通知信息”、“联系方式”等相对于核心域更稳定的概念。这方面的改进会在下文的精化过程中进一步描述。

《软件方法》第8章 分析 之 分析类图(2)

图8-58 当前的UMLChina类图中的非核心域概念(被圈住部分)

核心域透镜也有助于减少前文所说的开发人员迷恋“底层”的现象。例如要思考“电子邮件”的内涵,未必就是要思考SMTP、POP3/IMAP,从核心域透镜的视角看,思考关系链、信息载体等概念对开发好当前系统更有帮助。

8.1.6.7 属性要直接描述类

类和属性连在一起说"类的属性",应该能直接说得通,否则类和属性的搭配是不合适的。这个时候应该找到或建立合适的类,把该属性移进去。“属性要直接描述类”这个要求和关系数据库的第三范式“任何非主属性不依赖于其它非主属性”相似。

例如图8-59,“联系人的组织名称”中间隔了个“组织”,不能直接说通,需要添加一个“组织”类,把“名称”挪过去。

《软件方法》第8章 分析 之 分析类图(2)

图8-59 属性要能直接描述类

在缺少抽象的“照猫画虎”式建模中,这种错误比较常见。例如,一张工作居住证上确实有该人员聘用单位名称。建模人员对照工作居住证,一项一项把它搬到类图上的类中。

如果确定每个联系人只就职于一个组织,而且系统只关注组织的名称,可以将“名称”合并到“联系人”成为一个属性“组织名称”,如图8-60。不过,如果以上的假设发生变化,这样的做法应变成本很高。

《软件方法》第8章 分析 之 分析类图(2)

图8-60 特定条件下可以简化

要特别说明的是,习惯于关系数据库建模的建模人员有时会犯这样的错误,在一个类里放上另外一个类的属性作为“外键”。比如针对上面的例子,建模人员会想:“联系人”里放“组织名称”确实不合适,但是放个“组织编码”作为外键总可以吧?其实也不可以。"组织编码"是“组织”的属性,是封装在“组织”中的秘密,“联系人”不应该拥有“组织”的属性,它通过关联拥有“组织”对象,通过访问“组织”对象公开的操作间接访问“组织”的属性。

“联系人”里放“组织编码”不合适,放一个无意义的标识“组织ID”呢?同样也不可以。对象有标识,这是一个共识,而如何表达对象的标识,这是另一个领域的知识,而且实现规律和核心域知识无关。分析类中不需要主键、外键属性。

在设计工作流,需要把类图映射到关系数据库时,确实需要把"组织"表的主键(可能是"编码"也可能是系统生成的代理主键)放在"联系人"表中作为外键,但正如上文所说,这同样是另一个领域的知识,而且映射规律和核心域知识无关。

《软件方法》第8章 分析 之 分析类图(2)

图8-61 不需要外键属性

下面我们用“属性直接描述类”检验规则检查一下UMLChina系统的例子:

(1)用例规约中提到“联系人当前所在城市所属分区与公开课举办城市所属分区相同”——“联系人”的“城市”的“分区”,需要如图8-62分解。

《软件方法》第8章 分析 之 分析类图(2)

图8-62 分离不直接描述类的属性——UMLChina系统例1

(2)用例规约中“通知任务的创建人和创建时间”。实际上是“通知任务”的“创建事件”的“时间”、“通知任务”的“创建事件”的“创建人”,而这个创建人就是公司助理。此时应分解为三个类,考虑到一个通知任务只有一个创建事件和它关联,而且不考虑创建事件的其他属性,可以把“创建”合并到“通知任务”中,如图8-63。

《软件方法》第8章 分析 之 分析类图(2)

图8-63 分离不直接描述类的属性——UMLChina系统例2

有时,属性不直接描述类的情况比较隐蔽。如图8-58中的“公开课”类,说“公开课的主题”,“公开课的大纲”是可以的,但是实例化后会发现,很多“公开课”对象的“主题”和“大纲”是一样的,不同“公开课”对象的不同属性值主要体现在“开始日期”、“结束日期”和“城市”上。当发现针对一些属性,有很多对象的值相同,而且这些属性刚好组成了一个领域概念,应该分离出这个概念。如图8-64,可以分离出“课程”,把这两个属性移到“课程”中。

《软件方法》第8章 分析 之 分析类图(2)

图8-64 分离有大量相同值的属性到另一个类——UMLChina系统例子3

这里经常会有异议,认为“公开课”也应该有“主题”和“大纲”属性,因为一门课程的主题和大纲会随时间变化,我们需要像快照一样,复制课程当时的属性值,记住在某时某地举办公开课时的主题和大纲,否则维护的信息是不真实的。如果按照这样的思路,和公开课关联的所有类的属性都需要复制到公开课中,因为联系人的姓名、城市的名称也会改变。再推下去,就需要一个巨大的类,把所有通过关联线连在一起的类的所有属性组合在一起。

应对这种情况的一种做法是针对特别需要关注的视图另外加报表类,例如“公开课通知”,这个报表类和“公开课”、“课程”不关联,而且对象的值不会变化。当然,如果一开始没有创建某个视图的报表,对象的值发生变化后,想从其他视图推导出该视图不一定行得通。

如图8-65所示,报表ReportA、ReportB、ReportC、ReportD记录了某个时间点A、B、C不同属性组合的情况。假设现在需要一种新的报表,里面包含属性a2,想要追溯某个时间点上a2相关的记录已经是不可能的。因为a2已经变化,其他报表没有保存下来。

《软件方法》第8章 分析 之 分析类图(2)

图8-65 从不同的类提取属性组成报表

但这样的情况也是正常的,系统仅仅需要映射领域的一小部分知识。比起记住“某个姓名曾经在某个城市名称上过课“来说,记住“某人曾经在某个城市上过课”更加本质。

如果要更充分地记录历史,可以针对“课程的主题和大纲发生变化”这个领域事实建模,也就是说,为对象建立不同的版本,或者记录对象所有的属性值变化,如图8-66。

《软件方法》第8章 分析 之 分析类图(2)
《软件方法》第8章 分析 之 分析类图(2)

图8-66 跟踪对象的属性值变化

(3)如图8-67所示,“发件邮箱”的“最小时间间隔”。这个“最小时间间隔”在很多“发件邮箱”对象中值相同,说明它其实不直接和具体每个发件邮箱相关,而是和发件邮箱的规格相关。例如,如果我们设定,针对1*3.com免费邮箱,发送邮件的最小时间间隔为70秒(通过了解该类邮箱的发件限制规则来推算这个时间值),那么就算注册了100个1*3.com免费邮箱,这个值都是一样的。同样,SMTP服务器、POP3服务器……等属性的值,也只和发件邮箱的规格相关,和具体的每个发件邮箱无关。应该分离出“发件邮箱规格”,和“发件邮箱”关联。

《软件方法》第8章 分析 之 分析类图(2)

图8-67 分离有大量相同值的属性到另一个类——UMLChina系统例子4

前文提到有一种属性是状态属性。针对状态属性的检查原则刚好反过来——连在一起说"属性的类"应该能直接说得通。例如,“待举办”的“公开课”、“已通知”的“联系人”。严格来说,状态属性不是真正的属性,最后应尽量用状态机来封装状态转换的逻辑,然后删除状态属性。后文会专门阐述。

8.1.6.8 属性在本领域内不可以再分解

复杂属性

如果属性再分解就得到其他领域的概念,那么这个属性可以留在类中。如果可以继续分解成本领域的概念,可以考虑把这个属性独立出去变成另一个类。

如图8-68中,"联系人"的"称呼"属性的类型是String。String属于基础语义领域,已经不属于人员管理领域,那么"称呼"可以留在“联系人”中作为属性存在,而"组织"还可以像右侧所示分解成“名称”、“办公地址”等,这些概念依然属于人员管理领域,所以可以考虑将“组织”分离为一个类,“联系人”关联到“组织”。

《软件方法》第8章 分析 之 分析类图(2)

图8-68 分离可以在本领域内分解的属性

注意:分离或不分离的理由是“是否另一个领域”而不是“是否简单”。就拿“称呼”分离到String来说,String其实不简单。以.NET Framework 4.5为例,其中的String类有123个操作,远远超过人员管理领域程序员编写的某个类所拥有的操作。

多重性大于1的属性

另一种可以再分解的形式是多重性大于1的属性。例如,一个人员会有多个电话,如果放在“人员”类中作为“电话1”、“电话2”、“电话3”……属性,该类的很多对象的“电话3”属性可能会出现空值,或者放不下更多的电话号码,还有人会建模成一个“电话”属性,属性值里用逗号分隔各个电话号码,这样也模糊了属性的含义。此时应该把多重性大于1的属性分离到另一个类,设置要进一步抽象,如图8-69。

《软件方法》第8章 分析 之 分析类图(2)

图8-69 分离多重性大于1的属性

有的建模人员会在“人员”里放上一个数组或列表,这样做也是不对的。人员有多个电子邮件,这是一个领域的知识;用编程语言如何实现一对多的关联,是另一个领域的知识。

8.1.6.9 属性对所有对象都有意义

前文说到“类的属性”的检验规则如果说不通,那么类和属性放在一起是不合适的,但这只是必要条件,不是充分条件,即使说得通也未必合适。如图8-67,人的姓名,人的▲▲(▲▲是男性特有的器官),人的〇〇(〇〇是女性特有的器官)好像都说得通,但如果问:是不是所有对象都应该有这个属性呢?得到的答案就不同了。

是不是所有人都应该有姓名——是。

是不是所有人都应该有▲▲——不是,只有一部分人有。

是不是所有人都应该有〇〇——不是,只有一部分人有。

说明“人”发生了分裂,分裂成“男人”、“女人”两个子集(子类)。

《软件方法》第8章 分析 之 分析类图(2)

图8-67 分解只属于部分对象的属性到子类

《软件方法》第8章 分析 之 分析类图(2)

扫码或访问http://www.umlchina.com/book/quiz8_1_2.htm完成在线测试,做到全对以获得答案。

《软件方法》第8章 分析 之 分析类图(2)

1. 为什么面向对象分析设计方法比面向过程好?

 A) 面向对象更适合人脑去把握系统的复杂性

 B) 面向对象和需求的映射更直接

 C) 面向对象方法更容易掌握

 D) 面向对象更符合计算机的底层

2. 以下给类和属性命名,最合理的是__________

《软件方法》第8章 分析 之 分析类图(2)
   B 
《软件方法》第8章 分析 之 分析类图(2)
   C 
《软件方法》第8章 分析 之 分析类图(2)
   D 
《软件方法》第8章 分析 之 分析类图(2)

3. 以下说法正确的有(多选):

A 实体-关系图和数据流图也可以描述分析模型

B 和设计工作流的对象相比较,分析工作流的对象的特点是仅存在于内存中,不保存到硬盘

C 每个用例映射一个分析边界类

D 识别分析类时,精力应该重点放在实体类上

E 识别分析类时,类名称以涉众常用的称呼为准

F 系统外部有执行者,使用面向对象方法分析,系统内部一定有相应的实体类

4. 铁路售票处,售票员使用售票系统来售票,在用例进行过程中,系统需要不断向旅客反馈车次、车票和价格信息,系统还需要和银行系统交互。"售票"用例的分析序列图中,会出现_____个边界类,_____个控制类,_____个实体类。

 A) 1,2,3

 B) 3,1,2

 C) 不定,1,3

 D) 3,1,不定

 E) 3,2,3

 F) 3,1,3

 G) 不定,1,不定

 H) 3,3,3

5. 从以下用例规约抽取类,哪些类应该抽取出来?

游客选择航线、航期,

系统反馈该航期的剩余仓位。

游客选择仓位所在层,

系统反馈该层平面图。

游客选择仓位,

系统验证该仓位可以预订,

系统保存仓位预订信息,

系统反馈预订成功。

A) 层

B) 仓位保存

C) 航线

D) 仓位验证

E) 系统

 F) 仓位

6. 当我们把待开发系统称为“系统”时,说明我们在思考________问题:

A 业务建模

B 需求

C 分析

D 设计

7. 当我们把待开发系统称为“UMLChina系统”时,说明我们在思考________问题:

A 业务建模

B 需求

C 分析

D 设计

8. 要实现验钞机的“验钞”功能,恰当的抽象是?

A) 

《软件方法》第8章 分析 之 分析类图(2)
B) 
《软件方法》第8章 分析 之 分析类图(2)
C) 
《软件方法》第8章 分析 之 分析类图(2)
D) 
《软件方法》第8章 分析 之 分析类图(2)

8.2 步骤3-2 识别类之间的关系

目前我们已经得到的工作成果是图8-46。接下来,我们开始讲解如何识别类之间的关系。

首先要说明的是:先识别类和属性、再识别类之间的关系这个思考顺序只是一个微小的思考周期内的顺序,而要建模一张类图,需要很多个思考周期。也就是说,识别类和属性→识别类之间的关系→识别类和属性→识别类之间的关系→……是交错进行的。阅读用例规约或其他素材,一边思考一边建模,不管识别出类、属性还是关系,画上去就是,并不需要假装看不见类的关系先只识别类和属性,等画完了类和属性再识别类之间的关系。

类之间的关系有三种:泛化(Generalization)、关联(Association)和依赖(Dependency)。依赖是一个大杂烩。可以这样认为,如果B变化,A也需要变化,但A和B之间没有泛化或关联关系,那么A依赖于B。

8.2.1 泛化和关联的区别

泛化和关联是面向对象的两种基本复用机制。在泛化关系中,子类通过继承超类而拥有超类的特征;在关联关系中,对象通过组装其他对象而拥有其他对象的特征。如图8-68所示。

《软件方法》第8章 分析 之 分析类图(2)

图8-68 面向对象的两种基本复用机制

泛化表示集合关系,两个类形成泛化,意味着超类的对象集合包含了子类的对象集合;而关联表示个体关系,两个类形成关联,意味着一个类的对象个体组装了另一个类的对象个体。如图8-69所示。

《软件方法》第8章 分析 之 分析类图(2)

图8-69 泛化和关联的本质区别

集合关系还是个体关系,这是泛化和关联的本质区别。仅仅从自然语言的表达来推断,很多时候是不可靠的。

例如,自然语言"人有男有女"说的是泛化关系。因为意思不是一个人的个体组装了若干男人个体和若干女人个体,而是说人的集合包含了男人的集合和女人的集合。但是,自然语言"人有手有脚"说的却是关联关系。因为意思不是人的集合和手、脚的集合有包含关系,而是说一个人的个体组装了若干手和脚的个体。您可以自行体会一下“人有车有房”和“人有高富帅有屌丝”的区别。

对于比较熟悉的领域,例如刚才的男女手脚,拍脑袋就可以知道是泛化还是关联,那拍脑袋就可以了。如果进入陌生的领域,有时回溯到集合和个体的本质区别是必要的。下面列举一些错例参考。

泛化被误作关联的情况是比较多的:

图8-70中,“员工有调度员、装卸工、配货员”指的是员工的对象集合包含了调度员、装卸工、配货员的对象集合,不是指一个员工对象由调度员对象、装卸工对象、配货员对象构成。正确的关系是右侧的泛化关系,而不是左侧的关联(此处是组合)关系。

《软件方法》第8章 分析 之 分析类图(2)

图8-70 泛化被误作关联 例1

很多系统经常需要设置一些参数,有人会把参数建模成图8-71左侧的类图,把超时时间、锁定设置、频带等作为参数的属性。属性其实就是关联(此处是组合)的一种变体,8-71左侧和右侧是等同的。

《软件方法》第8章 分析 之 分析类图(2)

图8-71 泛化被误作关联 例2

图8-71的意思是一个参数个体由若干个具体参数个体组成,这不符合领域内涵。更符合领域内涵的是“具体参数是参数的一种”或者“参数的集合包含各具体参数的集合”,也就是说,泛化关系更合适。还有一种做法是把具体的参数全部抽象为“名称”和“值”两个属性。如图8-72。

《软件方法》第8章 分析 之 分析类图(2)

图8-72泛化被误作关联 例2 更正

如果按图8-71的方式建模,参数类只有一个对象,但这个对象有很多个属性。当需要为系统设置一种新的参数时,就需要修改类结构,增加新的属性。如果按图8-72的方式建模,只需要增加新的参数对象即可,类结构不需要改变。

一些1对0..1的关联,有可能是泛化关系。例如,有人认为1台电器可以是1台洗衣机,也可以不是;1台电器可以是1台电视机,也可以不是;1台电器可以是1台空调,也可以不是,于是画出图8-73。

《软件方法》第8章 分析 之 分析类图(2)

图8-73的意思是一台电器可能由一台洗衣机、一台电视机、一台空调组装而成,这是错误的,应该是电器的集合包含洗衣机、电视机和空调的集合,即泛化关系。如图8-74。

《软件方法》第8章 分析 之 分析类图(2)

图8-74 泛化被误作关联 例3 更正

关联被误作泛化的情况:

几个类拥有相同的部分时,有人可能会把相同的部分变成超类,和这几个类形成泛化关系。如图8-75,经理、组长和组员都有账户,于是把账户提升为超类,意思是“经理是账户的一种”或“账户的集合包含经理的集合”,这是错误的。

《软件方法》第8章 分析 之 分析类图(2)

图8-75 关联被误作泛化 例1

经理和账户的正确关系应该是关联(此处是组合),即使有泛化关系,也应该抽象出更合适的领域概念,例如“人员”,如图8-76。

《软件方法》第8章 分析 之 分析类图(2)

图8-76 关联被误作泛化 例1 更正

图8-77是Meilir Page-Jones在他的书中举的一个极端的例子。

《软件方法》第8章 分析 之 分析类图(2)

图8-77关联被误作泛化及更正 例2

8.2.2 识别泛化关系

8.2.2.1 识别泛化的思路

直接形成

首先,类图中的两个类可能会直接形成泛化关系,如图8-78所示。严格的做法是针对每两个类,思考“A是B的一种吗?”,再反过来思考“B是A的一种吗?”不过如果真的要这样做,工作量还是挺大的。类图中有n个类,就需要思考2C2 n=n(n-1)次。n=11时,就是100次了!实际工作中,往往是先扫描一遍,大脑迅速过滤出可能值得这样思考的类,针对这些类思考即可。

《软件方法》第8章 分析 之 分析类图(2)

图8-78 直接形成-两个类之间直接形成泛化关系

像图8-78这样,类图上已有的两个类有泛化关系但未识别的情况并不多,因为之前从用例规约识别类和属性时很有可能已经发现了。

自下而上(从特殊到一般)

更多的情况是发现类图上已有的两个或多个类有共同特征,于是抽象出共同的超类,如图8-79所示。

《软件方法》第8章 分析 之 分析类图(2)

图8-79 自下而上-两个类之上有共同的超类

以UMLChina案例项目的领域为例,可能会存在如图8-80的自下而上的识别过程:

《软件方法》第8章 分析 之 分析类图(2)

图8-80 自下而上识别泛化 例子

关联实际上就是扩展的属性,如果多个类关联到同一个类,也可以考虑泛化出共同的超类。

自上而下(从一般到特殊)

如图8-81所示,这个识别思路就是“8.1.6.8 属性对所有对象都有意义”里的思路,此处就不再重复叙述。

《软件方法》第8章 分析 之 分析类图(2)

图8-81 自上而下-一个类分裂出子类

人类认识世界的过程就是自上而下(从一般到特殊)的过程。例如对生物的认识,原始人的概念很简陋,第一部辞书《尔雅》中已有简单的分类:草木虫鱼鸟兽,今天的生物分类学按域、界、门、纲、目、科、属、种分层,已经达到一个庞大的数字。

8.2.2.2 泛化进一步讨论

8.2.2.2.1 Liskov替换原则相关问题

可能您已经从一些书上看到过如图8-82的矩形和正方形问题,有时这个问题被换成椭圆和圆。

《软件方法》第8章 分析 之 分析类图(2)

图8-82 被广泛讨论的正方形矩形问题

图8-82中,正方形是矩形的子类。按照设想,设置矩形的A边长为4,再设置B边长为5,此时求面积得到4×5=20,但如果正方形的面积是20,边长应该是√20=2√5才对。

矩形和正方形的问题经常伴随着Liskov替换原则(LSP)的讨论。LSP是Barbara Liskov在1988年提出的关于基类型和子类型的原则[Liskov 1988],原文如图8-83所示。Bertrand Meyer[Meyer 1997]使用契约的观点解释了为什么正方形和矩形之间泛化关系违反了LSP:子类操作的后置条件弱于超类。

《软件方法》第8章 分析 之 分析类图(2)

图8-83 Liskov替换原则的原文

关于如何纠正,也有很多方案,但大多数是从实现技巧的角度来解决问题。我们尽量从领域知识的角度看问题。

矩形的定义是:至少有三个内角是直角的四边形。由此衍生的性质有:对边平行且相等、对角线互相平分且相等、面积=长×宽……这些是矩形的共性,没有问题。之所以出现冲突,是因为我们不知不觉地把常见矩形的特征(邻边可以不等长)当成了所有矩形的共性。更合理的泛化关系应该如图8-84所示。

《软件方法》第8章 分析 之 分析类图(2)

图8-84 更合适的矩形类层次结构

在图8-84中,正方形不是自由矩形的一种。自由矩形、正方形和黄金分割矩形(边长比为黄金分割比0.618····:1)等是互相不重叠的矩形子集(子类),而且各子集的并集等于超集(超类)。如果能遵循这样的思路,那么建立的泛化关系应该符合Liscov替换原则。

矩形对象的属性是封装的,外部调用者只能通过公开的操作修改和访问属性,如“设置A边(a)”、“设置B边(b)”、“计算面积()”等。

操作和属性不一一对应也不应该一一对应,这是面向对象的优点。对于有些矩形来说,“设置A边(a)”操作只是给A边长赋值为a,而对另一些矩形来说,“设置A边(a)”操作既修改了A边长,也修改了B边长,甚至有的矩形还不让修改呢!

以更常见的“银行账户”类来举例更容易帮助理解。“银行账户”类有一个属性“余额”,但对外不能提供“修改余额(金额)”、“修改状态(状态)”等操作,应该提供的是“存款(金额)”、“取款(金额)”等。

当外部调用者向“银行账户”对象发送“取款(2000)”消息时,引起“银行账户”对象内部的变化不仅仅有(1)余额减少了相应金额,可能还要有:

(2)减去0.1%的手续费;

(3)如果余额已经低于某个设置值,某个在内部表示状态的属性值会改变;

(4)创建一个和该账户关联的“交易”对象记录取款的细节。

改进后的矩形类图可以如图8-85所示。超类中实现“求面积”,但不实现“设置A边(a)”、“设置B边(b)”的操作,留给子类来实现。

《软件方法》第8章 分析 之 分析类图(2)

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

或者如图8-86,把超类“矩形”到底保留几个边长属性以及如何求面积的实现下放到子类。这样的处理相当于把邻边互有依赖的领域知识放在属性中,而不是放在操作中。

《软件方法》第8章 分析 之 分析类图(2)

图8-86 另一种矩形类图

在日常应用中也有类似于矩形和正方形的情况。例如,会员的Email和QQ可以自由变化,但有一种会员,我们姑且称为“腾讯专属会员”,他的Email只能用他QQ号下的QQ邮箱,Email和QQ两个属性之间有依赖,那么图8-87左侧关系是不合适的,应该改为右侧的关系。

《软件方法》第8章 分析 之 分析类图(2)

图8-87 会员和腾讯专属会员

8.2.2.2.2 尽量不要跨领域使用泛化关系

分析工作流的类建模关注的是核心域概念及其关系,但有时候建模人员会不自觉地引入非核心域的内容。例如,若干学员组成小组,建模人员想到了如何实现小组有多个学员的问题,决定用List来实现,于是有图8-88。

《软件方法》第8章 分析 之 分析类图(2)

图8-88的问题是把本来应该隐藏在背后的非核心域概念显式引入到核心域类图中。前文已经说过,域之间的映射往往是有规律的,即使实现时由于某种偏好就是要通过泛化来复用,也没有必要逐一画出来。当然,更合理的实现是通过关联来复用List,如图8-89所示。

《软件方法》第8章 分析 之 分析类图(2)

图8-89 通过关联来复用List

《软件方法》第8章 分析 之 分析类图(2)

继续阅读