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

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

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

 

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

在业务建模和需求工做流,咱们的思考焦点一直在待开发系统的边界外。如今,思考的焦点从外观过渡到内部机理。程序员

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

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

8.1.1 核心域和非核心域

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

系统微信

核心域概念网络

非核心域概念架构

文档处理器(如Microsoft Word)框架

文档、页、行、字……

CStringArray、CFileDialog、MSXML……

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

商品、订单、会员……

</div>、ActionForm、SessionFactory……

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

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

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

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

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

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

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

8.1.2 基于核心域的复用

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

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

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

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

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

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

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

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

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

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

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

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

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

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

在这方面,很多媒体有误导。媒体会访问某些"知名程序员"对建模的见解,获得的回答多是"对我来讲不重要"。这里面的缘由是:基础设施领域的程序员更容易获得媒体青睐成为"知名程序员", "芯片"、"操做系统"、"编译器"等词汇上的光环更容易撩拨媒体从业人员的兴奋点。

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

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

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

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

图8-7 另外一个“底层”——藏在涉众心底里的各类但愿和担忧

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

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

图8-8是一款Windows 10认证的主板的广告。今天咱们买硬件,硬件包装盒上会写"兼容Windows 10",你们对此已经习觉得常。其实细想起来是比较奇怪的,按道理应该是软件兼容硬件,怎么反过来了?由于Windows在操做系统领域的优点大于该硬件在本身领域的优点。

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

8.1.3 分离核心域和非核心域

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

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

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

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

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

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

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

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

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

图8-11 核心域类图

若是将图8-11中的Person类映射为C#实现,可能会获得图8-12的C#代码[4]

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

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

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

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

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

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

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

 


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

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

★有利润的系统,其内部都是复杂的。千万不要幼稚地觉得"个人系统不复杂"。

图8-15 复杂的状态机图

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

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-16 "最小可行产品"(minimum viable product)开发过程

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

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

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

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

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

8.1.4 三种分析类

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

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

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

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

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

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

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

构造型

责任

和用例的关系

命名

边界类

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

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

外系统名称+接口

控制类

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

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

用例名称+控制

实体类

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

用例和实体类的关系是多对多的,一个用例能够由一到多个实体类协做实现,一个实体类能够参与一到多个用例的实现。

领域概念名称

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

8.1.5 识别分析类和属性

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

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

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

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

固然,咱们画类图的目的不只是为了获得数据库,面向对象和数据库也没有必然的绑定关系。任何系统均可以用面向对象的方式来构造,无论它用什么方式来持久存储对象。

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

int destinationFloor=5;

可是,这样的作法,背后的类型是int,这是基础设施领域的概念,不是电梯调度领域的概念,说明咱们的复用级别是基于基础设施域,没有基于核心域。图8-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-25 UMLChina系统类图1

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

工做流

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

缘由

业务建模

某某系统

研究对象是组织。组织中有不少系统,须要指出系统的名字。

需求

系统

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

分析

不少个类

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

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

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

图8-27 无心义的类图

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

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

图8-28 UMLChina系统类图2

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

图8-29 UMLChina系统类图3

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

图8-30 UMLChina系统类图4

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

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

图8-31 UMLChina系统类图5

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

图8-32 UMLChina系统类图6

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

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


图8-33 UMLChina系统类图7

⑩联系人。映射为类。

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

⑫公开课。映射为类。

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

图8-34 UMLChina系统类图8

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

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

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

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

图8-35 UMLChina系统类图9

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

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

图8-36 UMLChina系统类图10

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

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

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

图8-37 UMLChina系统类图11

步骤

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

补充约束

字段列表

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

2. 邮件主题的模板:

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

2.邮件内容的模板:

[联系人称呼]您好,

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

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

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

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

[报名交费信息]

[大纲]

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

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

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

图8-38 UMLChina系统类图12

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

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

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

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

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

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

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

图8-39 UMLChina系统类图14

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

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

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

图8-40 UMLChina系统类图14

步骤

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

补充约束

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

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

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

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

图8-41 UMLChina系统类图15

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

  1a1. 用例结束

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

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

  1b2. 用例结束

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

  1c1. 用例结束

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

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

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

1d3. 用例结束

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

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

图8-42 UMLChina系统类图16

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

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

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

图8-43 UMLChina系统类图17

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

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

乘客提供身份标识

系统验证身份标识合法

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

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

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

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

步骤

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

补充约束

业务规则

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

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

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

图8-45 UMLChina系统类图18

步骤

7. 系统保存通知任务

补充约束

字段列表

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

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

图8-46 UMLChina系统类图19

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

weixinpanjiayu2.jpg