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

墙上挂了根长藤,长藤上面挂铜铃

《长藤挂铜铃》;词:元庸,曲:梅翁(姚敏),唱:逸敏,1959

 

8.1 步骤3-1 识别类和属性

在业务建模和需求工作流,我们的思考焦点一直在待开发系统的边界外。现在,思考的焦点从外观过渡到内部机理。

《软件方法》第8章 分析 之 分析类图(1)_第1张图片

图8-1 思考系统的内部机理

系统如何构成,仅仅是软件开发组织考虑的事情,涉众不需要在意——只要系统能满足需求规约里阐明的各种需求。系统为了满足这些需求,必须封装一定的知识。如何组织这些知识,才能让建模人员的大脑更好地把握系统的复杂性,是分析和设计的关键。

8.1.1 核心域和非核心域

一个软件系统封装了若干领域的知识,其中一个领域的知识代表了系统的核心竞争力,是系统和其他系统区分的关键所在。这个领域称为"核心域",其他领域称为"非核心域"。更通俗的说法是"业务"和"技术",但使用"核心域"和"非核心域"更严谨。[1]核心域不一定是物流、医疗、金融等非计算机领域,也可以是计算机和软件领域。图8-2展示了不同系统的核心域和非核心域概念:

系统

核心域概念

非核心域概念

文档处理器(如Microsoft Word)

文档、页、行、字……

CStringArray、CFileDialog、MSXML……

电子商务网站(如淘宝网)

商品、订单、会员……

、ActionForm、SessionFactory……

图8-2 不同系统的核心域、非核心域概念

随着信息化的深入,组织内部封装在软件(即业务实体)中的领域逻辑比例越来越大,深度越来越深,组织之间的竞争越来越依赖于软件的竞争。市场的激烈竞争,又使得组织越来越聚焦于一个领域,为组织提供软件的软件组织也越来越专注于一个领域,甚至逐渐成为组织里的一个部门。将来,独立的软件组织也许将不复存在,或者说,所有组织都是软件组织。从当前的趋势看,软件的运行形态越来越"互联网化",说"所有组织都是互联网组织"也可以。成熟的"互联网公司"都有自己清晰的领域定位,京东是商场,新浪是媒体……

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

图8-3 软件组织位置的变化

以2015-2017年我上门提供服务的组织为例,名字带"软件"、"科技"、"网络"等词语的组织比例已经不到一半,更多的是其他行业组织,例如“***汽车技术有限公司”、“航天**集团第*研究院”、“**市规划国土房产信息中心”、 “**飞机设计研究院”、“**银行软件中心”……。

如果软件在组织中的分量不重,关键的领域逻辑还是封装在人脑中,使用一些通用软件就足够了,例如Microsoft Office、QQ、微博、微信。Microsoft Office没有为某个行业而定制,协和医院和北京四中买到的Microsoft Office是一样的。如果我们要的不仅仅是"书写文档"的软件,而是要"编写采购计划"的软件,也就是说,软件中要封装"采购计划"的领域逻辑,这就不是Microsoft Office能胜任的。[2]这个时候,组织需要的是封装了"采购计划"相关逻辑的"采购业务系统"。

每个组织都有自己独特的"文化"。组织的员工不是标准化的,而是要适合组织的"文化",这样才有办法在竞争中获胜。引进的软件系统和员工一样,也要体现组织的"文化",所以不同组织可能还需要不同特色的"采购业务系统"。

综上所述,市场上需要花样繁多的各种系统,这是竞争和分工导致的必然结果。很多系统之间可能在关键的点上有微妙差别,但许多内在机制是类似的。如果能高效复用这些机制,软件组织就能以较低成本变出各种系统来满足市场。

8.1.2 基于核心域的复用

 "复用"一词实际上没有听起来的那么阳春白雪,设计的目的就是复用。如图8-4所示,软件的开发是动态变化的过程,从第一个功能,到第二个功能……第一个版本,到第二个版本……第一个系统,到第二个系统……。组织发展到一定程度,甚至要维护许多个系统组成的系统家族。设计的好坏之分在于,让已有系统满足新需求时,付出的代价有多大。换句话说,就是以前做的工作可以复用的比例有多高。

《软件方法》第8章 分析 之 分析类图(1)_第3张图片

图8-4 功能→功能集→系统族

有一些"敏捷"论调宣传"开发系统不需要设计,只需要把它分解成小块,一块一块地做就可以了"。按这样的说法,盖大楼也很简单,不需要画图纸,一块砖一块砖往上垒就是。可惜,高下之分就在于:有的团队稳稳地把楼盖上去了,而有的团队在盖第二层时,第一层出问题需要维修,好不容易弄好了,盖第三层时,下面两层又出问题……虽然最终有可能也把楼盖起来,但这样的水平能竞争过别人吗?

软件开发方法的发展史,就是不断提升复用级别的历史。比起很多年以前,现在的软件开发在图形界面、网络协议、数据存取等基础设施领域的复用上,已经达到了相当的高度。例如要做图8-5这样的一个点名抽奖工具,需要人工编辑的介质(即所谓"源代码")只需要几十行字符,其它部分的逻辑已经由基础设施封装了。

《软件方法》第8章 分析 之 分析类图(1)_第4张图片

图8-5 基础设施的复用已经到了一定高度

遗憾的是,目前大多数软件组织的复用仅停留在基础设施领域的复用,即使有自己的"内部开发平台",也仅是根据自己所开发系统的需要对基础设施作进一步封装。特别是"互联网公司",其津津乐道的"架构"往往就是基础设施的架构。

在一些软件开发大会常可以看到这样的场景:某电子商务网站的架构师上台讲了一通,接着某视频网站的架构师上台也讲了一通,咦,两个演讲内容如此相似?原来,他们讲的都是自己系统中非核心域的知识,根本不涉及核心域的知识。究其原因也许并非不为,而是不能——开发人员对自己所开发系统的核心域研究太浅。许多“网红程序员”在网上谈论的内容大多是某种语言或框架的新特性,少有探讨他当前所开发系统的复杂领域逻辑,也是同样的原因:并非不为,而是不能。

非核心域(也就是别人的核心域)的优势仅仅是暂时的,竞争对手也能通过同样的渠道获得。A采用了某种新的工具,短时间内获得了对B的竞争优势,但随后B也获得了该工具,A的竞争优势很快就消失了,利润流进了工具厂商的口袋。非核心域的改进是必要的,但不充分,还要在核心域上深入挖掘,让竞争对手无法轻易从第三方获得。对于软件组织来说,在核心域上深入挖掘,达到基于核心域的复用,是获得和保持竞争力的根本手段。

对软件开发组织里的个人来说,专注于某个核心域也越来越重要。过去说“我是一名Java程序员,我可以用Java来开发物流系统、保险系统、医院系统”,现在要说“我是一名物流领域的开发人员,擅长用Java实现”。

要做到这一点基于核心域的复用,有一定的难度,因为能带来利润的系统,往往被迫关注的领域比较多,"负载"比较高。

开发一个基础设施领域的系统,例如操作系统,只需要关注计算机的资源,不需要关注顾客、订单、病历等具体某个应用领域的概念。按道理,开发应用系统也应该可以不管基础设施,但遗憾的是,当前现实中大多数情况下还是要管。例如,开发一个"棒医生在线"网站,不仅仅需要具备医疗卫生领域知识,还需要懂Linux,懂MySQL,懂Apache……。

另外,基础设施领域有大量已出版教材和先行例子,高校也为计算机和软件相关专业学生开设了相应课程(Linus Torvalds就是在大学教材中MINIX案例的激发下编写了Linux)。这样,开发人员的大脑比较容易把握基础设施领域的复杂性,对显式建模的要求没有那么高。在2017年2月1日用"操作系统"为关键字搜索当当网(dangdang.com),得到10461种图书。其中一步步教读者如何自己编写操作系统的书也不在少数。

《软件方法》第8章 分析 之 分析类图(1)_第5张图片

《软件方法》第8章 分析 之 分析类图(1)_第6张图片

图8-6 当当网搜"操作系统"

很多能够带来利润的系统,它的核心域却没有那么多人去研究。很少有类似这样的书,把一家电厂的流程,各种概念之间的关系,用某种方式(UML的类图、序列图、活动图,以前的数据流图、E/R图)表达得清清楚楚。

在这方面,不少媒体有误导。媒体会访问某些"知名程序员"对建模的看法,得到的回答可能是"对我来说不重要"。这里面的原因是:基础设施领域的程序员更容易得到媒体青睐成为"知名程序员", "芯片"、"操作系统"、"编译器"等词汇上的光环更容易撩拨媒体从业人员的兴奋点。

开发团队A研发出了Aware,获得市场的认可,开发团队B利用Aware研发出Bware,也同样获得市场的认可。根据我们上面所说的,研发Aware和Bware各有各的复杂度。但是需要批评一种现象——开发团队B里的某个开发人员在使用Aware的过程中产生了错觉,以为研发Aware才是“技术”,把大量的精力用来思考Aware的核心域知识,却对Bware的核心域知识不屑一顾。不客气地说,媒体热爱的一些"知名程序员“就是以上描述的实例。一边拿着公司的薪水,却不好好思考如何吃透公司的核心域做好公司的项目,把大量精力投入到自己的小爱好上,在网络上博得名声。

★某开发人员喜欢钻研“底层”。明明本职工作是编写一段计费的C#代码,他偏偏要花时间深入研究到编译器、操作系统甚至硬件,而且确实也搞清楚了一些门道。虽然工作是耽搁了,但该开发人员却获得了“勤奋好钻研”的名声。其实还有另一个更值得钻研的“底层”:怎样才能使这段代码更容易维护和扩展?这段代码达到的功能和性能对涉众意味着什么?……

过分热衷于钻研“底层”,这样的行为更像是偷懒而不是勤奋,毕竟比起离开电脑去搞清楚质管部和生产部之间有什么利益上的冲突,研究MSIL的语法要容易得多,愉快得多。

所谓“底层”也只是另一个领域的知识,那个领域自有另外的人去研究。玩票式的钻研,在真正专注研究这个领域的研究者看来,实在是不值一提。但是人性的弱点如此,正如钱钟书所说:“蝙蝠碰见鸟就充作鸟,碰见兽就充作兽。人比蝙蝠就聪明多了。他会把蝙蝠的方法反过来施用:在鸟类里偏要充兽,表示脚踏实地;在兽类里偏要充鸟,表示高超出世。向武人卖弄风雅,向文人装作英雄;”[钱 1982]

《软件方法》第8章 分析 之 分析类图(1)_第7张图片

图8-7 另一个“底层”——藏在涉众心底里的各种希望和担心

和我们生活工作密切相关的软件,媒体关注得太少。一名白领,早上起来用微波炉热牛奶,开电视看新闻,坐电梯下楼,刷卡坐地铁,手机刷微信朋友圈,打卡进公司,用公司的业务系统工作。上面这句话中涉及到的七个系统中,估计只有微信的开发人员能登上媒体的版面。大多数开发人员做的软件和"知名程序员"不一样,让"知名程序员"来做这些软件,未必做得来。微波炉的软件是谁写的?Linus Torvalds能做好一个医院信息系统吗?好软件、复杂软件的判断标准是能带来利润的软件,不能主观地认为做A领域就比做B领域高级和复杂。做"电厂燃料管理系统"的开发人员没有必要仰视“××编译器”或“××操作系统”的开发人员。

市场经济中,不存在哪个领域比其他领域更核心。如果像过去"以粮为纲"、"以钢为纲"一样,扭曲市场信号,硬性指定某个领域(芯片、操作系统)更核高基,造就的多半是骗取纳税人金钱的投机分子。

图8-8是一款Windows 10认证的主板的广告。今天我们买硬件,硬件包装盒上会写"兼容Windows 10",大家对此已经习以为常。其实细想起来是比较奇怪的,按道理应该是软件兼容硬件,怎么反过来了?因为Windows在操作系统领域的优势大于该硬件在自己领域的优势。

《软件方法》第8章 分析 之 分析类图(1)_第8张图片

图8-8 广告:Windows 10认证的主板

8.1.3 分离核心域和非核心域

要达到基于核心域的复用,有必要将核心域和非核心域分开考虑,将分析和设计分开考虑。人脑的容量有限,过早把各个域的知识混杂,会增加不必要的负担,导致开发人员腾不出脑力来思考核心域中更深刻的问题。一些宣传"简单设计"、"敏捷设计"的文章和书籍,所举例子涉及到的领域逻辑也真是比较"简单"。

Martin Fowler在《重构》("Refactoring: Improving the Design of Existing Code")[Fowler 1999]的第一章举了一个影片出租店的例子,先展示快而脏的代码,然后再不断重构,得到更合理的结构,内容确实很容易打动新手。类图如图8-9所示。

《软件方法》第8章 分析 之 分析类图(1)_第9张图片

图8-9 《重构》中的例子

不过,如果具备一些领域建模知识,一眼就可以知道图8-9左侧类图犯了后文阐述的“照猫画虎”的错误,类图长得像用例图。系统的重要价值在于封装了Movie和Price之间的秘密。根本就不需要先走很多弯路再回正路。摸着石头过河是难免的,但应该在不得不摸的时候才摸,不应该假装看不见前人已修好的桥,无论大小事都主动追求摸着石头过河。

如图8-10所示,假设三个域要考虑的因素分别是a、b、c个,如果分开考虑,找到域和域之间映射的规律,负担最小可以变成a+b+c;如果混在一起考虑,大脑的负担最大会达到a×b×c。[3]

《软件方法》第8章 分析 之 分析类图(1)_第10张图片

图8-10 大脑负担的复杂度

更为复杂的是,同一个核心域可能要映射到多个互相竞争的非核心域,即所谓"多平台"。例如Sports Interactive的《Football Manager(足球经理) 2017》游戏[FM2017],就有PC版、Mac版、Linux版、iPad版、Android版。核心域和非核心域如果不能很好地分离,开发的成本会大大增加。

★软件不是从天上掉下来,是人脑开发的。人脑的容量和运算速度有限,待解决问题的规模一旦变大,就必须分而治之。我们可以想象,如果外星人占领了地球,改造人类,把人类的大脑容量和运算速度提升到当前的一亿倍,那么一个现在看起来非常复杂的系统,那时只需大脑一转就搞定了,不需要显式建模。可惜,外星人没来,就算人类中有天才,大脑比其他人好五倍,超过五倍的复杂度,也要和普通人一样服从客观规律。

我们看一个"人员管理"领域的类图,如图8-11所示。

《软件方法》第8章 分析 之 分析类图(1)_第11张图片

图8-11 核心域类图

如果将图8-11中的Person类映射为C#实现,可能会得到图8-12的C#代码[4]:

《软件方法》第8章 分析 之 分析类图(1)_第12张图片

图8-12 类的C#实现(用Enterprise Architect映射)

如果将图8-11中的类映射到关系数据库,会得到图8-13所示的数据库结构:

《软件方法》第8章 分析 之 分析类图(1)_第13张图片

图8-13 将类图映射到数据库模型(用Enterprise Architect映射)

核心域知识和非核心域知识是独立的,域和域之间的映射规律,与域中的个体不直接相关。如果将图8-11中的Person改成Dog,City改成Cat,映射的套路没有变化。如果我们调整了域之间的映射套路,映射结果也会按照我们的调整有规律地变化,与域中的个体依然无关。

一些建模工具如Enterprise Architect和Rhapsody,可以完成类图和状态机图到非核心域的映射。即使没有强有力的自动映射工具,开发团队也可以针对几个典型的用例,归纳出最佳映射套路,编写出实现。然后,将分析模型和典型用例实现作为案例训练程序员,让程序员能够举一反三,按图施工。

以上只是提到了核心域和非核心域的分离,并没有指定思考领域概念的方法和表达领域概念的形式。可以用面向对象的方式思考,也可以用面向过程、面向××的方式思考。思考的结果可以用类图表达,也可以用E/R图等其他图形表达,甚至可以用文本表达。不过,当领域逻辑复杂时,可视化展示的图形比起文本更能帮助人脑把握大局。

如图8-14的类图,和只有自上而下顺序的文本相比,二维图形更容易让开发人员看出这些类之间的规律,更好地切割系统。

 


《软件方法》第8章 分析 之 分析类图(1)_第14张图片

图8-14 售火车票的领域类图

图8-15是Miro Samek在他的书[Samek 2008]中举的计算器例子。小小计算器要做到没有漏洞,其中的思考也很复杂。如果不先用层次状态机的图形对领域逻辑显式建模,再根据模型通过工具或人工映射到实现,而是直接下手实现,领域逻辑靠临时脑补,得到的代码必定破绽百出。[5]

★有利润的系统,其内部都是复杂的。千万不要幼稚地以为"我的系统不复杂"。

《软件方法》第8章 分析 之 分析类图(1)_第15张图片

图8-15 复杂的状态机图

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

《软件方法》第8章 分析 之 分析类图(1)_第16张图片

1. 请把左边的软件组织和右边领域概念画线对应

1 明源软件                             a 农田、片块、变更、审批

2 上海数慧                             b 患者、医生、药品、药房

3 浙江联众                             c 售楼计划、价格管控、回款、诚意客户

A) 1-a,2-b,3-c 

B) 1-a,2-c,3-b 

C) 1-b,2-a,3-c 

D) 1-b,2-c,3-a 

E) 1-c,2-a,3-b 

F) 1-c,2-b,3-a 

2. 针对一个android上的点菜应用,请问以下哪些是核心域概念。(多选)

A) Dish 

B) Activity  

C) SQLiteDatabase  

D) Reservation  

E) Button  

F) Price

3. 如果有人说"Linux代码超过千万行,也没有用UML建模、面向对象之类的啊?",应该怎么回答比较好?

A) 人和人不一样,搞操作系统的是天才,不能比。

B) 操作系统领域的负载比较低。

C) 其实是用了UML建模的,只不过没有公布出来。

D) 因为Linux用了敏捷过程,敏捷以后就不用建模了。

4. 《程序员》杂志曾经刊登一篇译文,作者在白板上画了一个类图,然后开始掰着指头数这个类图缺什么,"没考虑到持久化","没考虑到对象的创建"……然后得出结论:画这个类图不如直接编码。请根据8.1.3的知识评价以上观点。

A) 不同意。作者不了解核心域和非核心域分离的重要。

B) 不同意。这个图会越来越细,逐渐添加作者认为缺少的那些东西。

C) 同意。Talk is cheap. Show me the code.

D) 同意。代码才是最终结果,其他事情都是浪费。

5. 以下是网络上较流行的描述"最小可行产品"(minimum viable product)开发过程的图(图片来自http://www.nickmilton.com/2015/07/lean-km-and-minimum-viable-product.html)。

《软件方法》第8章 分析 之 分析类图(1)_第17张图片

8-16 "最小可行产品"(minimum viable product)开发过程

从本章内容出发,该图作者可能存在的认识上的最大错误是:

A) 认为造汽车应该先从轮子造起。

B) 认为造滑板车一定比造汽车简单。

C) 认为应该小步改进,先给客户一个滑板车也是改进。

D) 认为客户目前停止不动,随便给个什么车都是救命。

8.1.4 三种分析类

本书采用面向对象的方法来构造系统——假设系统由"对象"这样一种东西构成,对象封装了数据和行为。在分析工作流,我们认为系统中的对象在一个虚的"对象空间"中运行。这个空间不是内存,也不是硬盘,只是人脑中的一个逻辑空间,将它想象成宇宙空间也未尝不可。在"对象空间"中,速度不是问题,对象的创建和对象之间的通信都非常快。

《软件方法》第8章 分析 之 分析类图(1)_第18张图片

图8-17 虚的"对象空间"

注意上文提到的"假设"二字。面向对象就是一个假设,如果不认可系统由对象构成,也可以开发出系统,只不过用的方法不是面向对象方法。面向对象的思考方式比其他方法如面向过程要好一点,原因不是计算机喜欢面向对象或者面向对象更接近于计算机的底层(计算机更"喜欢"人类用机器语言编码,一千万行指令写在一起依次执行),而是面向对象的思考方式和人类的认知相当贴近,更有利于人脑去把握问题的复杂性。

具有共同特征的对象集合归为"类"。归类是人类认知的一种基本技能,其哲学讨论可以追溯到柏拉图的理念论(Theory of Forms)[Plato]。

依照Ivar Jacoson[Jacobson 1992]的思想,在分析工作流我们进一步假设系统中存在三种类:边界类(Boundary Class)、控制类(Control Class)和实体类(Entity Class)。在模型中,我们通过不同的构造型(Stereotype)来表达。很多UML工具已经内置了这些构造型。即使不用构造型区分,从"某某界面","某某控制"等类的名字也可以了解该类在系统中扮演的角色。

《软件方法》第8章 分析 之 分析类图(1)_第19张图片

图8-18 三种分析类的构造型

图8-19展示了三种分析类的责任、和用例的关系以及命名。

构造型

责任

和用例的关系

命名

边界类

输入、输出以及简单的过滤

每个有接口的外系统映射一个边界类。

外系统名称+接口

控制类

控制用例流,为实体类分配责任。

每个用例映射一个控制类。

用例名称+控制

实体类

系统的核心,封装领域逻辑和数据。

用例和实体类的关系是多对多的,一个用例可以由一到多个实体类协作实现,一个实体类可以参与一到多个用例的实现。

领域概念名称

8-19 分析类的责任、和用例的关系以及命名。

"每个有接口的外系统映射一个边界类"里的"外系统"不仅仅包括系统执行者,还包括仅接受系统输出信息的外系统。以下面将要开发的"时间→发送公开课通知"用例为例,该用例进行过程中,系统会向软件开发人员发送公开课通知,同时还要向UMLChina助理反馈发送通知的进展。软件开发人员和UMLChina助理在这个用例中仅仅是接受输出,没有输入信息给系统,但系统可以分别设置一个边界类来封装向软件开发人员和UMLChina助理反馈信息的责任,如图8-20所示。

《软件方法》第8章 分析 之 分析类图(1)_第20张图片

图8-20 外系统映射边界类,用例映射控制类

分析工作流的边界类不暗示任何实现方案。在总责任相等的前提下,它和实现的映射是多样的,可以用图形界面实现,也可以用非图形界面(包括文本、声音……)实现。即使使用图形界面实现,也不能简单认为一个边界类对应一个窗体。一个边界类的责任可以拆解到多个窗体上,一个窗体也可以和多个外系统交互。如何组织这些责任,应该从外系统的角度来考虑,而不是从用例或实体类的角度来考虑

图8-21中,“助理接口”边界类被圈住的几个责任来自不同用例的步骤,但在使用图形界面实现时,可以放在面向助理的、通知专用的窗体中。

《软件方法》第8章 分析 之 分析类图(1)_第21张图片

图8-21 边界类责任的组织

类似的例子还有:一份申请,需要通过系统审批三次,也就是三个不同的用例。在图形界面实现中,可能不需要准备三个窗体,部门主管、财务、副总三个审批人可以在同一窗体上工作,但部门主管、财务、副总各自有对应的分析边界类。

如果某个外系统和系统的交互很多,对应边界类的责任可能会有很多。另一种做法是按"外系统+用例"的组合映射边界类,这样可以减少一个边界类上的操作个数。不过,这样的做法已经暗示“按用例来划分边界”,所以还是建议尽量保持一个外系统一个边界类,如果操作很多,可以将从外系统角度观察可能要分在一组的操作移到一起,EA等工具可以随意定制属性和操作的上下显示顺序。

控制类是可选的,如果在分配责任时发现控制类只起到传递的作用,没有起到分解和分配的作用,那么就可以把控制类去掉。

图8-22展示了三种分析类之间的协作。

《软件方法》第8章 分析 之 分析类图(1)_第22张图片

图8-22 三种分析类在系统中的协作

执行者先把消息发给边界类对象,边界类对象能履行的就履行,无法履行的责任,再发给控制类对象。控制类对象就像总裁办,不做具体工作,只是将责任分解后分配给实体类对象。实体类按照它们之间的耦合程度聚集成若干聚合(也有可能一个类单独形成聚合),控制类对象发送消息时,先发给聚合的整体对象(也称聚合根),再由聚合根分配给聚合内的其他对象。最后,由边界类对象反馈信息,完成一个交互回合。

边界类与执行者、控制类与用例的映射关系很明显,所以识别边界类和控制类不需要太多思考。思考的主要工作量应该花在识别实体类上。一个用例需要哪些实体类协作实现、如何协作,一个实体类会参与哪些用例的实现,这是一个多对多的映射,需要由分析员的大脑决定哪种映射最好。

有的分析方法学如ICONIX[Doug 2007]提倡Robustness Diagram,认为可以通过它来帮助寻找类。开发人员一用确实感觉很舒服,噼里啪啦就发现好多类,有一种"我已经取得了不小成绩"的错觉,不过要是仔细看看,就知道"发现"的多是边界类、控制类。这些类用不着刻意去发现,只要按照图8-19的套路映射即可。最难的工作——寻找实体类以及它们之间的协作,Robustness Diagram却是寥寥带过。所以,本书不推荐开发人员额外花时间画Robustness Diagram。应该把精力放在识别实体类上,画分析序列图时再直接按照上面的套路映射相应的边界类、控制类。

★建模的每一个成果都应该是经过艰苦思考得到的。轻易得到的内容可能就不需要优先花时间建模了,所以我们一定要对那些砍瓜切菜一样的建模方法和轻易得到的正确无用的废话心怀警惕。这些不假思索得到的东西,没有门槛,没有竞争力。

三种分析类的划分也同样只是一种思考的方式。不认可这种思考方式,也可以开发出系统,只不过系统的结构可能不那么好。边界类(B)、控制类(C)和实体类(E)的划分和实现中的MVC概念有不同,后文再讨论这个问题。

8.1.5 识别分析类和属性

目前已有的工件是用例规约,它可以作为识别类的开始。阅读用例规约的基本路径、扩展路径、字段列表和业务规则部分,针对表示名词或事件的词汇,逐个思考,这是不是系统要记住的概念?如果是,那么它是类,还是某个类的属性?

《软件方法》第8章 分析 之 分析类图(1)_第23张图片

图8-23 从用例规约提炼类和属性

★之前做需求启发时,如果为了整理领域知识画了类图,在此处可以挑系统相关的部分,结合用例规约的内容精化。

如果您有关系数据库建模的经验,也可以这样思考:如果系统采用关系数据库来保存数据,那么数据库里应该会有哪些表?这样思考得到的表和实体类基本上是一一映射的。表对应类,列对应属性,行对应对象,关系对应关联。如果数据建模技能掌握得好,得到的数据模型符合1NF、2NF和3NF,那么用数据建模的思考方式得到的类图极有可能也是合格的。反过来也可以说,如果类建模做得好,映射得到的关系数据库模型也会有合理的结构。

当然,我们画类图的目的不仅是为了得到数据库,面向对象和数据库也没有必然的绑定关系。任何系统都可以用面向对象的方式来构造,不管它用什么方式来持久存储对象。

例如,我们坐电梯上楼时,在电梯里按了按钮5。电梯到了5层,会把门打开。电梯肯定记住了某些东西才能这么做。可以认为它记住了一个整数5,代码会这样写:

int destinationFloor=5;

但是,这样的做法,背后的类型是int,这是基础设施领域的概念,不是电梯调度领域的概念,说明我们的复用级别是基于基础设施域,没有基于核心域。图8-24表达了电梯调度系统的恰当抽象。

《软件方法》第8章 分析 之 分析类图(1)_第24张图片

图8-24 电梯调度领域概念

图8-24中的规律只和核心域(电梯调度领域)有关,和如何用非核心域映射无关。例如,一部电梯去往多个目标楼层,这"多个目标楼层"在电梯对象里用数组、列表还是集合来表示,不影响核心域的概念。

接下来将从《软件方法(上)》给出的UMLChina系统两个用例的用例规约提炼类。先把两个用例规约列出如下:

用例编号:UC1

用例名:发送公开课通知

执行者:时间(主)

……

基本路径

1. 当到达时间周期时,系统选择下一个适合发邮件的发件邮箱以及下一个待发往的邮箱地址

2. 系统使用所选发件邮箱向所选待发往的邮箱地址发送公开课通知邮件

3. 系统记录邮件发送情况

扩展路径

1a. 没有正在生效的通知任务:

  1a1. 用例结束

1b. 有正在生效的通知任务,但没有指定发件邮箱:

  1b1. 系统向UMLChina助理的电子邮箱地址发邮件告知正在生效的通知任务没有指定发件邮箱

  1b2. 用例结束

1c. 有正在生效的通知任务,有指定发件邮箱,但没有适合发邮件的发件邮箱:

  1c1. 用例结束

1d. 没有下一个待发往的邮箱地址:

  1d1. 系统结束正在生效的通知任务

1d2. 系统向UMLChina助理的电子邮箱地址发邮件告知正在生效的通知任务已结束

1d3. 用例结束

字段列表

2. 公开课通知邮件=主题+内容

2. 邮件主题的模板:

[联系人称呼]您好,欢迎您参加[公开课举办城市][公开课开始日期]-[公开课结束日期]的[公开课主题]公开课

2.邮件内容的模板:

[联系人称呼]您好,

欢迎您参加[公开课举办城市][公开课开始日期]-[公开课结束日期]的[公开课主题]公开课

开课时间: [公开课开始日期]-[公开课结束日期]([周*、周*])(9:00-12:00,13:10-17:10)

上课地点: [公开课举办城市]

费用:每人[公开课费用]元,含午餐。交通、住宿费请自理。

[报名交费信息]

[大纲]

2. 发送邮件需要用到的信息:发件邮箱SMTP服务器地址、发件邮箱账户名、发件邮箱密码

3. 邮件发送情况=邮箱地址+发送时间+发件邮箱+是否成功

业务规则

1. 时间周期缺省为5秒

1. 定位适合发邮件的发件邮箱的规则:从正在生效的通知任务指定的发件邮箱中找出以下值最大而且值大于0的邮箱: (当前时间-邮箱上次发送时间)-邮箱最小发件时间间隔

1. 定位下一个待发往的邮箱地址的规则:针对正在生效的通知任务,随机选取符合以下条件的邮箱地址:联系人符合公开课通知任务条件,而且联系人有邮箱地址尚未被正在生效的通知任务通知

1. 联系人符合公开课通知任务条件的规则:联系人当前所在城市所属分区与公开课举办城市所属分区相同,而且联系人不属于"拒绝公开课通知联系人",而且联系人不属于该公开课"已通知联系人",而且联系人当前所在组织不属于该公开课"已通知的组织"

**********************************

背景知识:

为了尽可能减少干扰,公开课通知的总原则是:能不通知就不通知。

如果某位软件开发人员已经表明不想接到公开课通知,就不应再发公开课通知邮件给他,该人员成为"拒绝公开课通知联系人";如果某位软件开发人员已经在QQ、微信等其他途径咨询过某次公开课,也不应再发该次公开课的邮件给他,该人员成为该次公开课"已通知联系人";如果某组织的领导(例如研发总监、培训经理……)已经询问过某次公开课事宜,就不必再通知该组织里的开发人员该次公开课的信息,该组织成为该次公开课"已通知的组织"。

可以推测系统可能有另外的一个或多个用例会修改这些信息,但这些问题留到后面再考虑。

**********************************

用例编号:UC2

用例名:创建公开课通知任务

执行者: UMLChina助理

……

基本路径

1. UMLChina助理选择公开课,请求创建通知任务

2. 系统验证所选公开课适合创建通知任务

3. 系统反馈设置通知任务界面

4. UMLChina助理提交公开课通知任务

5. 系统反馈公开课通知任务

6. UMLChina助理确认

7. 系统保存通知任务

8. 系统反馈已经创建通知任务

扩展路径

2a. 所选公开课已存在正在生效的通知任务:

  2a1. 系统反馈所选公开课已存在正在生效的通知任务,询问是否停止所选公开课正在生效的通知任务

  2a2. UMLChina助理选择停止所选公开课正在生效的通知任务

    2a2a.选择不停止:

      2a2a1. UMLChina助理选择不停止所选公开课正在生效的通知任务

      2a2a2. 用例结束

  2a3. 系统停止所选公开课正在生效的通知任务

  2a4. 返回3

字段列表

4. 通知任务=公开课+{发件邮箱}*+{已通知的组织}*+{已通知的联系人}*+是否立即生效

7. 通知任务=4+创建时间+创建人

业务规则

2. 公开课适合创建通知任务的规则:该公开课没有正在生效的通知任务,而且公开课的开始日期应该是当前日期的3天或更长时间之后

接下来,我们来抽丝剥茧,逐句分析用例规约。

步骤

1. 当到达时间周期①时,系统②选择下一个适合发邮件的发件邮箱③以及下一个待发往的邮箱地址④

补充约束

1. 时间周期缺省为5秒⑤

1. 定位适合发邮件的发件邮箱的规则:从正在生效的通知任务⑥指定的发件邮箱⑦中找出以下值最大而且值大于0的邮箱: (当前时间-邮箱上次发送时间⑧)-邮箱最小发件时间间隔⑨

1. 定位下一个待发往的邮箱地址的规则:针对正在生效的通知任务,随机选取符合以下条件的邮箱地址:联系人⑩符合公开课通知任务条件,而且联系人有邮箱地址尚未被正在生效的通知任务通知

1. 联系人符合公开课通知任务条件的规则:联系人当前所在城市所属分区公开课举办城市所属分区相同,而且联系人不属于"拒绝公开课通知联系人",而且联系人不属于该公开课"已通知联系人",而且联系人当前所在组织不属于该公开课"已通知的组织"

①时间周期。这是执行用例"发送公开课通知"的时间周期,可以看作和时间交互的边界类“时间接口”的一个属性。

《软件方法》第8章 分析 之 分析类图(1)_第25张图片

图8-25 UMLChina系统类图1

②系统。"系统"的概念是需求工作流的概念。在需求工作流,我们把系统看作一个整体对外提供服务。在分析工作流中,"系统"的概念已经被打碎成各个类,所以"系统"这个词不需要识别成类。图8-26表达了不同工作流视角下的待开发系统。

工作流

如何称呼当前要开发的系统

原因

业务建模

某某系统

研究对象是组织。组织中有很多系统,需要指出系统的名字。

需求

系统

研究对象是当前要开发的系统,不需要再说名字。

分析

很多个类

研究焦点进入系统的内部,思考系统内部的构成。

图8-26 各工作流如何称呼当前要开发的系统

有些开发人员在这里会犯错误,把"系统"识别成一个类,画成这样:

《软件方法》第8章 分析 之 分析类图(1)_第26张图片

图8-27 无意义的类图

这种图只是简单功能分解的另一个变体,对剖析系统的复杂性没有帮助,却给开发人员带来一种虚假的成就感:我描述了几个类之间的关系,而且还是组合关系,已经开始剖析系统的复杂性了呢!

③下一个适合发邮件的发件邮箱。"发件邮箱"映射为类。"下一个适合发邮件",映射为类的状态属性,深入建模后,再消除这些状态属性。

《软件方法》第8章 分析 之 分析类图(1)_第27张图片

图8-28 UMLChina系统类图2

④下一个待发往的邮箱地址。"邮箱地址"本来应该是"联系人"的一个属性,但"下一个待发往的"这个定语说明"邮箱地址"有"下一个待发往"这样的状态属性,另外考虑到联系人会有多个不同用途的邮箱地址,所以把"邮箱地址"独立出来变成一个类,"下一个待发往"作为"邮箱地址"的状态属性。

图8-29 UMLChina系统类图3

⑤时间周期缺省为5秒。映射为"时间周期"属性的缺省值。

图8-30 UMLChina系统类图4

为了防止滥发邮件,邮箱提供商会规定每个邮箱每天发送邮件总量以及发送时间间隔,如果违反规定,邮件会暂时无法发出,邮箱甚至会被关闭。过于频繁地检测是否有符合条件的发件邮箱,没有意义,但如果检测时间间隔太长,导致可以发邮件时发件邮箱却空置,也影响发邮件的效率。5秒应该是合理的值。

⑥正在生效的通知任务。"通知任务"映射为类,"正在生效"映射为状态属性。

《软件方法》第8章 分析 之 分析类图(1)_第28张图片

图8-31 UMLChina系统类图5

⑦指定的发件邮箱。"通知任务"关联到"发件邮箱",同时调整一下类的位置。

《软件方法》第8章 分析 之 分析类图(1)_第29张图片

图8-32 UMLChina系统类图6

⑧上次发送时间。"发件邮箱"的属性。

⑨最小发件时间间隔。"发件邮箱"的属性。


《软件方法》第8章 分析 之 分析类图(1)_第30张图片

图8-33 UMLChina系统类图7

⑩联系人。映射为类。

⑪符合。映射为“公开课”和“联系人”之间的关联。

⑫公开课。映射为类。

⑬通知。映射为“邮箱地址”和“公开课”之间的关联。

《软件方法》第8章 分析 之 分析类图(1)_第31张图片

图8-34 UMLChina系统类图8

1. 联系人符合公开课通知任务条件的规则:联系人当前所在城市所属分区公开课举办城市所属分区相同

⑭联系人当前所在城市。"城市"映射为类,和"联系人"建立关联。"当前所在城市"是关联中"城市"的角色名称。

⑮所属分区。"分区"映射为类,和"城市"建立关联。

⑯公开课举办城市。"公开课"和"城市"建立关联,"举办城市"是关联中"城市"的角色名称。

《软件方法》第8章 分析 之 分析类图(1)_第32张图片

图8-35 UMLChina系统类图9

⑰"拒绝公开课通知联系人"。"拒绝公开课通知"映射为"联系人"的一个状态属性。

⑱该公开课"已通知联系人"。在"联系人"和"公开课"中间再加一个名称为"通知"的关联。深入建模后,会慢慢精简这些关联。

《软件方法》第8章 分析 之 分析类图(1)_第33张图片

图8-36 UMLChina系统类图10

⑲联系人当前所在组织。"组织"映射为类,和"联系人"建立关联。"当前所在组织"是关联中"组织"的角色名称。

⑳该公开课"已通知的组织"。在"组织"和"公开课"中间再加一个名称为"通知"的关联。

这时,类图上的类已经比较多了,我们调整类图中各个类的位置,让关联线尽可能不交叉。

《软件方法》第8章 分析 之 分析类图(1)_第34张图片

图8-37 UMLChina系统类图11

步骤

2. 系统使用所选发件邮箱向所选待发往的邮箱地址发送公开课通知邮件

补充约束

字段列表

2. 公开课通知邮件=主题+内容

2. 邮件主题的模板:

[联系人称呼]您好,欢迎您参加[公开课举办城市][公开课开始日期]-[公开课结束日期]的[公开课主题]公开课

2.邮件内容的模板:

[联系人称呼]您好,

欢迎您参加[公开课举办城市][公开课开始日期]-[公开课结束日期]的[公开课主题]公开课

开课时间: [公开课开始日期]-[公开课结束日期]([周*、周*])(9:00-12:00,13:10-17:10)

上课地点: [公开课举办城市]

费用:每人[公开课费用]元,含午餐。交通、住宿费请自理。

[报名交费信息]

[大纲]

2. 发送邮件需要用到的信息:发件邮箱SMTP服务器地址发件邮箱账户名发件邮箱密码

公开课通知邮件。"公开课通知邮件"映射为类。

主题+内容。"主题"、"内容"映射为"公开课通知邮件"的属性。

《软件方法》第8章 分析 之 分析类图(1)_第35张图片

图8-38 UMLChina系统类图12

联系人称呼。"称呼"映射为"联系人"的属性。

公开课开始日期。"开始日期"映射为"公开课"的属性。

公开课结束日期。"结束日期"映射为"公开课"的属性。

公开课主题。"主题"映射为"公开课"的属性。

公开课费用。"费用"映射为"公开课"的属性。

报名交费信息。"报名交费信息"映射为"公开课"的属性。

大纲。"大纲"映射为"公开课"的属性。

《软件方法》第8章 分析 之 分析类图(1)_第36张图片

图8-39 UMLChina系统类图14

SMTP服务器地址。"SMTP服务器地址"映射为"发件邮箱"的属性。

发件邮箱账户名。"账户名"映射为"发件邮箱"的属性。

发件邮箱密码。"密码"映射为"发件邮箱"的属性。

《软件方法》第8章 分析 之 分析类图(1)_第37张图片

图8-40 UMLChina系统类图14

步骤

3. 系统记录邮件发送情况

补充约束

3. 邮件发送情况=邮箱地址+发送时间+发件邮箱+是否成功

系统记录邮件发送情况。"邮件发送"映射为类。

邮件发送情况=邮箱地址+发送时间+发件邮箱+是否成功。添加"时间"和"成功"属性,建立"邮件发送"和"邮箱地址"之间的“发往”关联,建立"邮件发送"和"发件邮箱"之间的“使用”关联,建立"邮件发送"和"公开课通知邮件"之间的关联。

如果一封发往某邮箱地址的电子邮件发送失败,可以再次发送,使用的发件邮箱可以是之前的发送用过的发件邮箱,也可以是新的邮箱。也就是说,同一封电子邮件直到发送成功为止可能会发生多次发送事件,关联到哪个发件邮箱是由各次发送事件决定的。电子邮件发往哪个邮箱地址,只和电子邮件有关。

《软件方法》第8章 分析 之 分析类图(1)_第38张图片

图8-41 UMLChina系统类图15

1a. 没有正在生效的通知任务:

  1a1. 用例结束

1b. 有正在生效的通知任务,但没有指定发件邮箱:

  1b1. 系统向UMLChina助理的电子邮箱地址发邮件告知正在生效的通知任务没有指定发件邮箱

  1b2. 用例结束

1c. 有正在生效的通知任务,有指定发件邮箱,但没有适合发邮件的发件邮箱:

  1c1. 用例结束

1d. 没有下一个待发往的邮箱地址:

  1d1. 系统结束正在生效的通知任务

1d2. 系统向UMLChina助理的电子邮箱地址发邮件告知正在生效的通知任务已结束

1d3. 用例结束

UMLChina助理的电子邮箱地址。"助理"映射成类,"邮箱地址"映射成“助理”的属性。此处没有将“助理”关联到现成的“邮箱地址”,因为公开课通知不需要发往助理的邮箱地址,联系人的邮箱地址需要懂得的属性它不需要知道,例如"下一个待发往"、"某次公开课已通知"。当然,这个“邮箱地址”属性后面还会再精化。

通知任务已结束。"已结束"映射为"通知任务"的状态属性。

《软件方法》第8章 分析 之 分析类图(1)_第39张图片

图8-42 UMLChina系统类图16

第一个用例分析完毕,接下来看第二个用例。

1. UMLChina助理选择公开课,请求创建通知任务

UMLChina助理。UMLChina助理是系统执行者,首先映射一个边界类"助理接口"。

《软件方法》第8章 分析 之 分析类图(1)_第40张图片

图8-43 UMLChina系统类图17

这里要注意一点:“UMLChina助理”执行者和"助理"类并没有必然的对应关系,也就是说系统执行者和实体类没有必然的对应关系。类图中之所以存在“助理”类,是因为系统要维护助理的信息。

例如,乘客坐电梯上楼,乘客是电梯系统的执行者,但电梯系统可能不需要"乘客"实体类,因为它不需要记住乘客的信息。当然,有朝一日,电梯升级为维稳电梯,用例规约里有:

乘客提供身份标识

系统验证身份标识合法

系统记录乘客信息和入厢时间

这时,电梯系统里就有"乘客"实体类了,因为系统要记住乘客的信息。某个概念是否映射实体类的依据是系统是否要记住它,和是否有同名的执行者无关。

和执行者对应的是边界类。电梯系统没有"乘客"类,但会有"乘客接口"类,目前的实现形式多为图8-44所示。

《软件方法》第8章 分析 之 分析类图(1)_第41张图片

图8-44 乘客接口的虚与实

步骤

2. 系统验证所选公开课适合创建通知任务

补充约束

业务规则

2. 公开课适合创建通知任务的规则:该公开课没有正在生效的通知任务,而且公开课的开始日期应该是当前日期的3天或更长时间之后。

该公开课没有正在生效的通知任务。在"公开课"和"通知任务"之间建立关联。

当前日期。和本领域没有特定关系,不识别。

《软件方法》第8章 分析 之 分析类图(1)_第42张图片

图8-45 UMLChina系统类图18

步骤

7. 系统保存通知任务

补充约束

字段列表

7. 通知任务=4+创建时间+创建人

通知任务=4+创建时间+创建人。"创建时间"映射为"通知任务"的属性。在"通知任务"和"助理"之间建立关联,"助理"的角色为"创建人"。

《软件方法》第8章 分析 之 分析类图(1)_第43张图片

图8-46 UMLChina系统类图19

至此,用例规约里的概念已经提炼完毕。接下来的工作就是进一步精化类图,在此之前,我们来看看识别类和属性时的一些要点。

weixinpanjiayu2.jpg

你可能感兴趣的:(分析,UML,面向对象,建模,um类图类图,需求分析,领域驱动设计,DDD,pdf,敏捷开发)