第9章 分析 之 分析类图进阶
高坡砍柴要留桩,平地起房要留窗
《野合万事兴》;词:左小祖咒,曲:左小祖咒,唱:左小祖咒、宝罗,2008
本章在假设读者已经掌握类建模基本知识的前提下,讲述一些可以帮助建模人员得到更高质量类模型的进阶建模技能。
9.1.1 彩色建模思想概述
1999年,Peter Coad、Eric Lefebvre和Jeff De Luca在"Java Modeling In Color With UML"一书中阐述了将颜色用于UML建模的思想。在这本薄薄的、彩色印刷的书中,作者们认为各个领域之间存在一些领域中性(Domain-Neutral)的基本结构,所以使用四种颜色架构型(Archetype)来描述领域内的各种概念,并试图总结颜色之间的交互规律,为领域建模提供清晰的思路。
正如Peter Coad在书中所说,彩色建模根据他之前归纳的建模模式拓展得来。1992年,Peter Coad就在“Object-Oriented Patterns”一文中阐述了四个架构型,在后来出版的“Object Models: Strategies, Patterns, and Applications”书中也有应用,只是没有加上颜色。
图9-1 Peter Coad1992年阐述架构型的文章
本书中讲述的彩色建模技能基于Peter Coad等人的工作,加上了自己的一些理解,所以有些地方和原著有区别。
彩色建模的四种颜色架构型分别为事物(PartyPlaceThing)、描述(Description)、角色(Role)和时刻时段(MomentInterval),如图9-2所示。
图9-2 四种颜色架构型
如果观察各个领域,常会观察到这样的关系:
(1)“时刻时段”发生了,“事物”们扮演不同的“角色”参与进来。
(2)“事物”变化的规律和“描述”有关。
例如,当前您在做的“阅读”事件就是一个时刻时段,发生在某个时间,而“阅读”需要“阅读者”和“读物”两个角色参与,这两个角色分别由“软件开发人员”和“软件开发书籍”这两个“事物”扮演。“软件开发人员”读什么样的“软件开发书籍”,很可能是由“软件开发人员类型”和“软件开发书籍类型”之间的关系决定的。如图9-3所示。
图9-3 四种架构型的关系
首先要说的是,颜色架构型只是一些建模的提示和建议。不是所有的领域都会有图9-3的关系,如果您所关注的领域没有找到图9-3的关系,也不必生搬硬套,有些类不知道怎么涂颜色就不涂也无所谓;另外,关系也不一定非得像图9-3,也有可能是“时刻时段-描述”、“事物-事物”等。
9.1.2 事物(PartyPlaceThing)
“事物”在这里的含义并非“看得见摸得着的东西”,而是“状态最丰富的东西”。万事万物只要我们乐意,都可以找出它的状态变化,但是一个特定的系统往往是围绕一个或几个关键概念的状态变化而展开的。
图9-4是某个设备管理系统的部分类图。
图9-4 某个设备管理系统的部分类图
可以判断,该系统是围绕着“设备”的状态变化而展开的,那么,可以把“设备”涂成绿色。如图9-5所示。
图9-5 给“设备”涂上颜色
很容易画出“设备”的状态机图,如图9-6所示。
图9-6 “设备”的状态机图
从以上可以看出“事物”架构型的特点:
(1)它的状态非常值得关注。
(2)在其身上发生领域事件。
9.1.3 描述(Description)
“事物”的许多特征并不由“事物”本身决定,而是由背后的“描述”决定。
以图9-5中的类为例,单位采购了10台品牌型号完全相同的设备,每一台设备都要编号区分,而且“可借”、“故障”等状态也各自不同,但是,设备的品牌型号以及各种参数是一样的。这些内容应该分离到“设备规格”类中。这个“设备规格”就可以看作“描述”,涂色如图9-7。
图9-7 给“设备规格”涂上颜色
“事物”和“描述”的分离,大大减少了冗余的信息。对于结构复杂多变的“事物”,可以在“描述”级别定义好规则,“事物”级别按照规则执行即可。常见类图如图9-8所示。
图9-8 “事物”和“描述”的分离
例如,应用在“公文”上,可以得到图9-9。
图9-9 公文和公文模板的分离
从以上可以看出“描述”架构型的特点:
(1)对象个数较少。
(2)封装某方面的规则。
(3)状态变化不值得关注。
9.1.4 时刻时段(MomentInterval)
“时刻时段”是带有时间属性的类,相当于“事件”类。
这些类就像录像机一样,把围绕着“事物”发生的各种事件录下来。既然是录像机,录下来的信息是不好修改的,要么留着,要么删掉。
我们给上面的设备管理系统类图中的“时刻时段”类涂上颜色,得到图9-10。
图9-10 给“时刻时段”架构型涂上颜色
从图9-10可以看到,我们给每个粉红色的类都加上了时间属性。“设备的时间”或“设备规格的时间”说不通,但“租借的时间”就说得通了。
从以上可以看出“时刻时段”架构型的特点:
(1)对象个数多。
(2)属性值不应该被修改。
9.1.5 角色(Role)
“角色”放在“事物”和“时刻时段”之间,起到解耦的作用。“角色”的表示可以是显式的,也可以是隐式的。
我们给上面的设备管理系统类图加上“角色”,得到图9-11。
图9-11 给设备管理系统类图加上“角色”
像图9-11这样显式表示“角色”太占地方了,如果不需要为“角色”类分配责任,可以把它缩到关联的角色中,如图9-12所示。
图9-12 隐式表示“角色”
9.1.6 颜色的作用
给类标上颜色后,我们就可以使用一些常见的套路来帮助建模,包括类图建模和序列图建模的套路。
套路1:从“时刻时段”开始建模,逐步找出后面的“角色”和“事物”,如图9-13所示。
图9-13 从“时刻时段”开始推导
“时刻时段”是最容易观察到的,从“时刻时段”开始推导,比较符合人的思考习惯。将这个套路应用到上面的设备管理系统例子,如图9-14所示。
图9-14 从“时刻时段”开始推导设备管理系统的类
套路2:从“描述”之间的关系开始建模,然后在其上添加“事物”,如图9-15所示。
图9-15 从“描述”开始推导
“描述”之间的关系很可能描述了系统关注的领域规则。只要把规则描述清楚了,剩下的无非是在规则上玩游戏而已。将这个套路应用到上面的设备管理系统例子,如图9-16所示。
图9-16 从“描述”开始推导设备管理系统的类
在责任分配上,也会有如图9-17的套路。
图9-17 架构型之间的责任分配
将这个套路应用到上面的设备管理系统例子,如图9-18所示。
图9-18 架构型之间的责任分配应用到设备管理系统
在使用颜色来标记类时,要谨记同样一个类在不同上下文中颜色可以不同。还是以上面的设备管理系统为例,如果设备的租借需要收押金并按租借时长收费,费用的支付情况是系统非常关心的,那么可以把“租借”涂成绿色,“租借”上发生的事件则变成了粉红色,时间属性移到粉红色的类上。类图和“租借”的状态机图如图9-19所示。
图9-19 “租借”变得重要时的颜色和状态机图
9.2.1 模式
长期以来在软件开发人员中存在一个误解:模式=设计模式=GoF23模式。我提供服务的这些年,经常碰到这样的情况:
客户:潘老师,来给我们讲讲设计模式吧!
我:我猜你说的设计模式就是那23个模式吧,设计可以讲,模式也可以讲,这些都有用,光讲那23个没用的。
客户(愕然):啊?设计不就是学那23个设计模式吗,不是说学会了23个设计模式就掌握设计了吗?
其实GoF23模式不是最有用的,也不是最本质的,只不过这些模式比较早(也并非最早)写在书上,碰巧这本书在所有模式出版物中最出名。我们可以用中国象棋做类比。《橘中秘》是名气最大、翻印版本最多的古代象棋书籍,里面归纳了很多象棋的“模式”,但该书不是最早的,里面的知识也不是最有用和最本质的。很难想象现代人学习中国象棋会直接拿起《橘中秘》来学习。
图 9-20 GoF设计模式和《橘中秘》
模式的世界远远比GoF要大,不同的工作流、不同的平台都有人归纳了不同的模式,如图9-21所示。
图9-21 各种各样的模式
Hillside Group主办的PLoP(Pattern Languages of Programs,程序设计的模式语言)、EuroPLoP会议每年都举办,是软件模式领域最重要的会议。POSA系列和PLoPD系列模式书籍都已经各自出到了第5集。如图9-22所示。
图9-22 模式会议和POSA、PLoPD系列模式书籍
开发人员应该重点学习自己当前项目所处核心域的分析模式以及所用实现平台相关的设计模式,而不是死盯着GoF模式。例如,如果在.net平台下做一个预订机票的系统,那么应该重点搜索“机票预订的分析模式”以及“.net平台的架构模式”相关资料来学习。
9.2.2 常用变化:泛化和关联的转换
如果改变抽象的级别,泛化关系可以用更抽象的类之间的关联关系代替。例如我们可以说“人分为男人和女人两种”,也可以说“人有性别”,如图9-23所示。
图9-23 泛化关系可以用更抽象的类之间的关联关系代替
如果重点是数据的表达,例如统计人口,那么图9-23右侧的关联就够了,“人”有一个属性“性别”,不需要泛化关系;如果重点是行为的变异,例如游戏中男人和女人行为区别很大,应该考虑使用泛化关系。
很多模式通过把泛化转成关联来简化模型,套路如图9-24。
图9-24泛化转成关联的套路
下面我们来看一些常见的分析模式。
9.2.3 人员
人员的管理是很多系统不得不考虑的。假设要记录图9-25中人员资料,直接的做法就是照猫画虎得到图9-26。
图9-25 需要记录的人员资料
图9-26 照猫画虎的人员类图
从数据库设计的角度也可以看出图9-26中“电话1”、“电话2”等违反了第一范式,如果有更多的“电话”需要记录怎么办呢?可以考虑把这些多重性大于1的属性分离出来,如图9-27所示。
图9-27 分离多重性大于1的属性
图9-27有一些改进,但还是很难应对变化,其他属性如QQ、微信可不可以多个呢?完全有可能。而且,随着时代的不断进步,人的属性不断进化,许多以前关注的属性现在已经不再关注,例如传真、MSN等等,但是多了很多新的属性,例如微信、微博。如果采用图9-27的结构,就需要删除或增加“人员”的属性或关联线。
一种改进方法是添加泛化关系来隔离“人员”和具体联系方式属性,如图9-28所示。
图9-28 通过泛化隔离变化
从图9-24的套路可知,这样的结构可以变化成图9-29。
图9-29 泛化转成关联
如果觉得图9-29不好理解,可以把类模型转成关系数据库模型,填上数据帮助理解。图9-30展示了映射到关系数据库之后,将图9-25所示人员的信息填充到“联系方式”表时的数据。
图9-30 关系数据库里的数据表示
进一步抽象之后,一些概念也可以理得更清楚。例如,人员之所以有若干个电话、手机、微信……,原因可能是用途不同。如果直接映射到模型,就会得到“办公电话”、“住宅电话”、“办公手机”、“私人手机”等属性。做了图9-29的抽象之后,直接在“联系方式”上关联一个类“用途”即可,如图9-31所示。
图9-31 指明联系方式的用途
有些联系方式类型之间允许绑定。例如,电子邮件地址可绑定QQ帐号、MSN账号;从QQ号可以推导QQ邮箱地址和微信;从手机号可以推导139邮箱和微信,等等。注意,上面说的“可以”指的是联系方式类型的规则,具体联系方式是否绑定是不一定的。图9-32通过添加自反关联,表达了“可以绑定”和“绑定”的知识。
图9-32 指明联系方式的绑定关系
联系方式还有验证合法性的问题。通过以上抽象,这个问题也简单了。验证合法性的规则和具体的联系方式无关,只和联系方式类型相关。假设用正则表达式验证,可以有:
电子邮件:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*
电话:\d{3}-\d{8}|\d{4}-\d{7}
手机:^[1][3,4,5,7,8][0-9]{9}$
把正则表达式作为“联系方式类型”的属性,可以大大简化用于表达行为的代码。
人员之间存在各种关系。图9-33是一张网络上流传甚广的人际关系图。
图9-33 娱乐圈的复杂人际关系
图9-33相当于一张UML对象图,类其实只有一个:人员。人员之间存在的各种人际关系可以用自反关联表达,如图9-34。
图9-34 用自反关联表达人际关系
当人际关系类型较多时,“人员”类周围就会围上很多个自反关联的圈圈,可以引入“人际关系类型”,把这些圈圈合并成一个,圈圈之间的区别在“人际关系类型”中描述。如图9-35。
图9-35 抽象出人际关系类型
如果觉得图9-35不好理解,可以把类模型转成关系数据库模型,填上数据帮助理解。图9-36展示了映射到关系数据库并填充数据后的情况。
图9-36 关系数据库数据示例
抽象出“人际关系类型”后,就可以反映人际关系中的一些约束,例如:如果两人性别相同,那么这两人之间不该出现夫妻关系。类图如图9-37。
图9-37 人际关系中的约束