1. 首页
  2. IT教程

软件方法(下)分析和设计第8章分析 之 分析类图——知识篇Part01(202204更新)

*为避免单篇文章篇幅过长,分成多篇以合集发布*


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

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

可到此处下载《软件方法》(下)目前公开的最新版本(合集发布完毕再提供下载):

http://www.umlchina.com/book/softmeth2.pdf

您在阅读《软件方法》时如果发现错误,欢迎通过微信umlchina2告知。如果作者认为有道理,决定在下一次发布时根据您的意见修改,每个错误将付给您5.12元报酬,并在书中说明您的贡献。报酬通过微信支付。

(1)任何您认为的错误都可以,包括错别字。

(2)同一错误仅支付最先指正者报酬。

(3)请根据最新版本作指正。

下册内容目前指正人有(按指正时间排序):吴佰钊、王周文、刘学斌、成文华、黄树成、李蜀斌、杨雪鸿、王书伟、高洪江、张志坚、龙燔、陈文飞。

从分析工作流开始,我们每个内容都分为两章。一章讲述建模知识,一章讲述建模知识如何应用在本书案例中。这样的分割主要考虑到更符合实际的工作。

例如,在讲解分析类图时,我们讲解知识的顺序是这样的:

(1)识别类和属性

(2)审查类和属性

(3)识别类之间的泛化

(4)识别类之间的关联

如果把案例剖析分解到每个知识点,为了让案例的剖析符合内容的顺序,可能就会出现这样的情况:

讲解完识别类和属性后,案例剖析时,先列出很多类和属性,但没有泛化和关联关系,因为类的关系还没有讲到,所以即使观察到,也故意不画上去;接下来,讲解完类之间的关系后,案例剖析时,再把关系加上。

这和实际工作不符合。实际工作中,以上列出的几项工作是交叉进行的。

为了避免造成误解,我们先完整地讲解知识部分,本书案例如果有和所讲解知识相关的内容,会随时引用。然后在案例部分,按照实际工作中的思考方式灵活应用前面所讲解的知识点。

8.1 分析工作流概述

8.1.1 知识的分离

在业务建模和需求工作流,我们一直把目标系统看作是一个整体,想办法推导出涉众在意的整体表现——需求。

系统为了满足需求,必须封装一定的知识。这些知识,没法从天上掉下来,需要软件开发人员一点一点放进去。接下来,我们将思考:

(1)如何准确表达系统需要封装的知识,让系统满足需求;

以及进一步

(2)如何合理组织系统需要封装的知识,低成本地让系统满足需求。

如果不能合理组织知识,当新需求到来时,准确表达也会越来越难。如果考虑到利润,很难停留在(1)而不追求(2)。

不管是纯粹在大脑里面打转转,还是借助了纸笔或建模工具来协助,以上的思考是逃不掉的。如果需要封装的逻辑很简单,人脑的容量和运算速度能够胜任,在大脑里打转转还可以勉强应付,但是,能带来利润的系统都是复杂的(参见《软件方法(上)》1.8.1市场没有小系统),借助纸笔或建模工具来显式表达思考的过程很有必要,毕竟大脑容量和运算速度比一般人高出一个数量级的天才是很稀罕的。

有的人故意不显式表达,声称“大脑思考就够了”,背后的真相可能不是天才而是遮羞——你让他显式表达,他也表达不出来,因为没有掌握思考的方法。

思考的方法,也就是理清和恰当分离系统所要封装的各个领域的知识的方法。

8.1.2 核心域和非核心域

一个信息系统封装了若干领域的知识,其中一个领域的知识是该系统不能抛弃或替换的,这个领域称为"核心域",其他领域称为"非核心域"。图8-1展示了不同系统类型的核心域和非核心域概念。

系统类型

核心域概念

非核心域概念(选择之一)

文档处理器

文档、页、行、字……

CStringArray、CFileDialog、MSXML……

电子商务网站

商品、订单、会员……

</div>、ActionForm、SessionFactory……

操作系统

处理器、内存、页面……

struct、char、int

图8-1 不同系统类型的核心域、非核心域概念

以文档处理器为例,开发Microsoft Word和LibreOffice Writer所使用的编程语言和组件不一样,但文档、页、行、字等核心域概念是一样的。即使回到计算机诞生之前或者去到未来,这些概念也依然存在。

关于“核心域”和“非核心域”,一种常用的通俗说法是"业务"和"技术",但"业务"和"技术"的说法不严谨。

有的开发人员在潜意识里是这样划分的:

*我懂且我感兴趣的知识→技术;(我懂Java编码,我对Java编码感兴趣,Java编码是技术)

*我懂但不感兴趣的知识→业务;(下单、收银、配送我懂一些,但不感兴趣,这些是业务)

*我不懂但感兴趣的知识→高科技;(我不懂深度学习,但很感兴趣,哇塞,高科技)

*我不懂且不感兴趣的东东→忽悠。(我不懂UML建模,也不感兴趣,妈的,忽悠)

有的开发人员则是这样划分的:

*和计算机无关→业务;

*和计算机有关→技术;

核心域不能以“懂”、“感兴趣”来判断。核心域不一定是非计算机领域,也可以是计算机领域,如图8-1中的操作系统。

另外,还要特地说明的是,本书中的“核心域”和Eric Evans以及DDD(领域驱动设计)话语体系中的“核心域”(Core Domain)意思不同。

本书中的“核心域”指信息系统中不可替换的那部分内容——这个以软件开发人员的知识是可以判断的。

DDD话语体系中,把“领域”(相当于本书中的“核心域”)划分为"核心域"、“通用子域”、“支撑子域”等,例如“Delivery”是核心,“Customer”是通用,“Billing”是支撑——这个划分已经超出了软件开发人员的知识,我不认为软件开发人员有能力以及有必要做这样的判断。

一家商场之所以能击败其他对手,原因未必是下单部分有什么不同,倒有可能是在配送环节下了大力气,或者客户服务抓得好。没有经过商业竞争的思考,武断地认为某个子领域是系统的“核心”是不合适的。

8.1.3 域之间的映射和协作

域和域之间的映射以及协作的规律,与域中的个体不直接相关。

例如,我们看一个"人员管理系统"的核心域类图,如图8-2所示。

软件方法(下)分析和设计第8章分析 之 分析类图——知识篇Part01(202204更新)

图8-2 "人员管理系统"的核心域类图

如果将图8-2中的Person类映射为C#实现,可能会得到图8-3的C#代码:

软件方法(下)分析和设计第8章分析 之 分析类图——知识篇Part01(202204更新)

图8-3 类的C#实现(用EnterpriseArchitect映射)

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

软件方法(下)分析和设计第8章分析 之 分析类图——知识篇Part01(202204更新)

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

如果采用某种对象-关系映射器框架(例如微软的Entity Framework),Person对象和数据库中的Person表里的一行可能会这样联系起来:

person1=context.Persons.Find(ID)

如果将以上内容中的Person改成Dog,City改成Cat,映射的套路没有变化。即使我们调整了域之间的映射和协作的套路,得到的结果也会按照我们的调整有规律地变化,与域中的个体依然无关。

平时我们看到的一些“架构”,就是域之间映射和协作的一些套路。图8-5是现在常被提起的一些“架构”,可能在很多系统中都会观察到,即使这些系统的核心域及非核心域都有不同。

软件方法(下)分析和设计第8章分析 之 分析类图——知识篇Part01(202204更新)

图8-5 一些常见的“架构”

既然域之间的映射有“套路”,过早地混合不同域的知识是不划算的。

如图8-6所示,假设三个域要考虑的因素分别是a、b、c个,如果分开考虑,然后再找到域和域之间映射的规律,负担最小可以变成a+b+c;如果混在一起考虑,大脑的负担最大会达到a×b×c。a、b、c都大于√3时,相乘肯定要大于相加的。

软件方法(下)分析和设计第8章分析 之 分析类图——知识篇Part01(202204更新)

图8-6 过早混合不同域的知识会增加大脑负担

把不同域的知识分开考虑,不等于“把整个系统分析完了再编码”——“害怕分析瘫痪”是一块常用于遮掩分析能力不足的遮羞布。可参见《软件方法(上):业务建模和需求》1.3节关于“迭代”的内容。

过早地混合不同域的知识,会加重开发人员大脑的负担,导致开发人员腾不出脑力来思考核心域中更深刻的问题,只好稍微折腾一下如图8-5的“域之间的架构”,心里安慰自己,我有“架构”了!却忘了,其实还没有触碰到最需要大脑去思考的核心域概念和逻辑。而这又很可能会被巧妙地当成遮羞布——不是我不思考,而是要想的事情太多了顾不过来啊!

而这种微妙心态的进一步发展,就会导致开发人员有意无意地混合不同域的知识,把复杂度弄成a×b×c,以此达成废话刷工作量——以最少的思考得到最多的“成果”。

*近年最时髦的就是借DDD话语体系刷工作量了。例如,刚找出一个类Order,然后周围就围上一圈OrderFactory、OrderRepository、OrderService……,洋洋得意地把工作量刷了好几倍。*

我经常听软件组织的架构师向我介绍他们所开发系统的“架构”,口沫横飞,说的基本上都是图8-5的“域之间的架构”。好啊,真棒,我知道了。还有呢?没了?

构思那些“域之间的架构”是某些基础设施厂商或者方法学家的工作,我们挑一个适合自己项目的套路用上就行了。有什么问题,可以去请教用这个套路用得好的先行者。

“域内部的架构”,那些核心域概念和复杂逻辑,这是系统最值钱的地方。要是我们没有办法理清楚,别人是帮不到我们的。这才是大脑最该用的地方!

8.1.4 应对变化,不要吃错药

平时开发人员常说要“应对变化”,甚至有的人还喊口号“拥抱变化”,但我们需要认真想一想,要应对的是什么样的变化?

我列出各种“不好了,需求变了!”的情况如图8-7。

“不好了,需求变了!”

最需改进的技能

备注

需求其实没变,只是需求人员的“认识”变了(病情没进展,只是之前的诊断错了)

业务建模、需求

最多

需求确实有变化(病情进展了)

功能需求 变了

分析

第二多

质量需求 变了

设计

设计约束 变了

设计

图8-7 各种“需求变化”

第一种情况实际上是最多的:需求其实没变,只是需求人员的“认识”变了。这种由于业务建模和需求技能不足导致的伪“需求变化”实际上是最多的。

要应对这样的“变化”,光是有分析和设计技能是没用的,需要提升业务建模和需求技能。这些技能已经在本书上册(2到7章)讲解。

*张三出现恶心、乏力、食欲减退等症状,村里的道士九叔给他诊断,认为他鬼上身了,需要搞一个驱魔仪式。九叔已经精研驱魔理论体系和实践多年,一手辟邪剑法已经练到星耀一级。

那也没用!因为张三得的是乙型肝炎。*

第二种情况则是第二多:功能需求变化,包括增加了一个用例、增加了一个步骤、输入输出的字段多了一项、某个步骤的业务规则做了调整……等。如果能恰当地建模系统要封装的核心域逻辑,使得核心域模型能精确体现核心域的内涵,会大大有助于应对这样的变化。

应对第二种情况,需要提升分析技能。

*如果充分了解肝脏的工作机制,当张三被诊断乙肝时,又观察到张三酗酒、熬夜,就可以预测病情可能很快会进展到肝硬化甚至肝癌,这对应变是有帮助的。*

第三种和第四种情况发生得就没那么频繁:质量需求和设计约束发生变化,例如响应速度、并发容量、运行平台的更换等。

一些以“领域驱动设计”为名的文章,所举例子就1-2个领域类,然后就开始讨论Entity、Service、Repository、DTO、六边形架构……不是说这个知识没用,问题是软件组织缺的是这个嘛?

这些文章以为自己在说“领域驱动设计”,其实说的是“企业应用架构模式”、“互联网系统架构模式”。

强调“领域驱动设计”,背后暗含的意思应该是缺少“领域驱动”而不是缺少“设计”,结果呢?不谈领域,谈仓储、工厂。

在一些软件开发技术大会常可以看到这样的场景:某电子商务网站的架构师上台讲了一通,接着某视频网站的架构师上台也讲了一通,咦,两个演讲内容如此相似?原来,他们讲的都是自己系统中“域之间的架构”,而不是核心域内部的机制。究其原因也许并非不为,而是不能——架构师对自己所开发系统的核心域研究太浅。

许多“网红程序员 ”在网上谈论的内容大多是某种语言或框架的新特性,少有探讨他当前所开发系统的复杂领域逻辑,也是同样的原因:并非不为,而是不能。

说了那么多,归纳起来就是一句话:

8.1.5 重视分析工作流

分析,就是从核心域的视角构思系统的内部机理。

在现在的很多软件组织中,分析工作流的技能是非常被忽视的。很多开发人员上手就直接编码,原因并不是软件开发项目的领域逻辑简单到了不需要分析的地步,或者他的大脑发达到了在大脑里就可以完成分析工作的地步,而是开发人员缺乏分析的技能,只好草草跳过,而且为了遮掩自己的无能,还会想各种办法来遮羞。

草草跳过的不只是分析,需求也是同样的待遇。很多需求调研就是走个形式。开发人员没有掌握需求技能,给他时间做,他也不知道怎么做,随便晃悠两下,就着急回去编码了,因为这个工作他比较熟。

用考试类比,考试时,前面几道题比较容易,扫一眼就可以写出答案。越往后题目越来越难,学霸会拿出草稿纸,列出已知条件,正推、逆推……理出解题思路,然后再答。

学渣就麻烦了,根本没有学习相关的知识和解题方法,怎么办?他会想出各种办法来遮羞:

遮羞利器(1):时间。

例如,抱怨考试时间太紧张,来不及思考,只好胡乱写个答案,甚至故意提前交卷,力图给人造成一种“如果时间允许,我是能做对的”的印象——真相是,给再多的时间也不会。

对应到软件开发,就是以“时间紧”、“敏捷”为借口掩盖自己没有能力剖析复杂逻辑的事实。

遮羞利器(2):空间。

例如,考试时故意选择不好写的笔和劣质的草稿纸,力图给人造成一种“如果纸和笔再好一点,我是能做对的”的印象——真相是,不会就是不会,给他再好的纸笔也不会。

对应到软件开发,就是借助“口头交流”、“白板”等容量小的介质,掩盖内容的苍白。白板就这么大,所以客观上你总不好意思让我用白板剖析复杂的逻辑吧?

伽罗瓦在决斗前一天晚上仓促写下自己的数学思想,不停哀叹“我没有时间了”。唉,早干嘛去了,不过伽罗瓦是真懂。

软件方法(下)分析和设计第8章分析 之 分析类图——知识篇Part01(202204更新)

图8-8 伽罗瓦决斗前一天的手书

费马在书的空白处写下“费马猜想”,还写“我确信我发现一种美妙的证法,可惜这里的空白处太小,写不下”,估计费马是忽悠。

此处提到此二人纯属作者关于“时间”、“空间”不够的随意联想,无其他含义。

遮羞利器(3)听起来就比较高大上了:重构。

上世纪80年代末,Bill Opdyke(https://cseweb.ucsd.edu/~wgg/Abstracts/gristhesis.pdf)和Bill Griswold(http://laputan.org/pub/papers/opdyke-thesis.pdf)等人归纳了一些调整代码结构的手法,称为“重构”,后经Martin Fowler等人推广而广为流传。

“重构”的知识可以看作是建模知识的一个子集。如果开发人员真的熟练掌握重构的手法,很多情况下他已经有能力直接建模领域逻辑得到更合理的结构,根本不需要先走很多弯路再回正路。

还是用考试类比:如果考生有能力察觉某个解答的“坏味道”并“重构”,那么轮到他做类似题目时,他也应该有能力“建模”题目的各种条件,理清解题思路后直接给出正确的回答,并不需要故意做错再改过来。

如果考生别有用心把“重构”当遮羞布,结合前面两个遮羞利器,就会出现“我本来打算把我的答卷'重构'一下,但是没有时间了”,“我本来打算把我的答卷'重构'一下,但是答卷写满了没空间了”。

开发人员可以照此办理——“我先写快而脏的代码,然后再重构”,然后祭出遮羞利器“时间”(此处“空间”不好祭出)——“没想到啊,时间来不及了”。

摸着石头过河是难免的,但应该在不得不摸的时候才摸,不应该假装看不见已有的路和桥,无论大小事都主动追求摸着石头过河,而且,很多人不是假装看不见路,而是真的看不见路——就是个睁眼瞎。要是开发人员以“重构”为理由拒绝思考,很可能他的“重构”也是空话。

不过,大脑不用思考,凭感觉摸着石头过河不停刷工作量,也是一种躺平的幸福。

8.1.6 分析相关历史的简单回顾

1958年,John W.Young Jr.和Henry K. Kent发表“Abstractformulation of data processing problems”,第一次提出在独立于实现的抽象级别上定义系统的规范。

软件方法(下)分析和设计第8章分析 之 分析类图——知识篇Part01(202204更新)

图8-9 摘自 “An abstract formulation of data processing problems”(Young JW, Kent HK,1958)

1959年,CODASYL(数据系统语言会议)成立。1962年,CODASYL提出了一个和Young/Kent类似的模型,称为“信息代数”(Information Algebra)。

1970-1980年代是结构化分析方法的时代,主要贡献者有Börje Langefors、Chris Gane、Trish Sarson、Tom DeMarco、Pin-Shan Chen、E. F. Codd等人。结构化分析的主要建模方法是数据流图和实体-关系图,这两者的结合,让软件开发人员有能力剖析大型系统。

软件方法(下)分析和设计第8章分析 之 分析类图——知识篇Part01(202204更新)

图8-10 摘自 Structured analysis and system specification(DeMarco T,1979)

软件方法(下)分析和设计第8章分析 之 分析类图——知识篇Part01(202204更新)

图8-11 摘自 The Entity–Relationship model: Towards a unified view of data(Chen PPS,1976)

1982年,Nastec公司开发出了DesignAid,这是第一款CASE(计算机辅助软件工程)工具。随后,其他CASE工具陆续出现。据PC Magazine的1990年1月30刊统计,当时已经有超过100家公司提供了将近200款CASE工具。

软件方法(下)分析和设计第8章分析 之 分析类图——知识篇Part01(202204更新)
软件方法(下)分析和设计第8章分析 之 分析类图——知识篇Part01(202204更新)
软件方法(下)分析和设计第8章分析 之 分析类图——知识篇Part01(202204更新)

图8-12 摘自PC Magazine 1990年1月30日刊(红框圈住的内容说明了工具的数量)

1980年代后期,面向对象的思想开始用于分析和设计。然后,UML统一了表示法。这部分历史已经在本书第1章“UML简史”部分讲述,此处不再赘述。

软件方法(下)分析和设计第8章分析 之 分析类图——知识篇Part01(202204更新)

图8-13 摘自 Object Oriented Analysis, 2nd Edition(Coad P,Yourdon E, 1990)

软件方法(下)分析和设计第8章分析 之 分析类图——知识篇Part01(202204更新)

图8-14 摘自 Object lifecycles. Modeling the world in states(Shlaer S, Mellor SJ, 1992)

8.1.7 互联网和敏捷的影响

互联网浪潮以及敏捷运动的冲击打断了分析方法学的传承。

互联网浪潮到来之前,信息系统的竞争焦点是功能。

我1997年毕业,先到高校当了一年老师,然后才去软件公司做程序员。第一个参与开发的系统是酒店管理系统。这样的系统用的人不多,服务器一台,每个部门放上一台客户端电脑就差不多了,但功能很多,入住、退房、收银、客房,餐饮、娱乐、财务、电话计费、各种报表等等,能不能把核心域逻辑理清楚非常关键。

互联网的兴起带来了这样一种系统:这种系统功能很简单,开发这种系统时需要思考的核心域逻辑很少,但是这样的系统可以通过互联网让非常多的人使用,问题的关键变成了“如何在大用户量下保持性能”。

典型的例子是1996年出现的hotmail,推出一年多时间就有1200万的用户。hotmail是一个基于web的电子邮件系统,这样的系统,开发出来并没有太大难度,竞争的关键在于有没有背景、有没有钱买基础设施,有没有钱做推广……。

可能有人会说“邮件系统也有逻辑啊!”当然,这同样是一个领域,也有逻辑,但是其中的绝大多数逻辑已经被前人探索得很清楚,甚至有实际的可用组件提供,并不需要web电子邮件系统的开发人员从头思考。

很多开发人员就进入了类似的“互联网公司”,开发或维护类似的系统。因为不需要剖析复杂的核心域逻辑,开发人员有没有掌握分析的技能已经无所谓,于是,很多打着“敏捷”旗号的“方法”就在这类公司大行其道,导致软件开发人员的分析能力普遍退步。

经常有人和我说,潘老师,敏捷这一套做工厂管理系统之类的可能不太行,但不得不承认,做互联网很管用噢!

当然管用了!

有个巫医发明了一种治疗方法。他坦言,我这个方法对付癌症可能不太行,但对付感冒很管用噢!你不信,找个感冒患者来!

感冒患者找来了,医生让患者躺在一张绘有八卦图案的方桌上,然后绕着患者绕了八八六十四圈(看到没,他也是有一套方法的!),然后对患者说,回去该吃吃该喝喝,五天之内就好了!

果然,患者好了。

医生四处宣传他的治疗方法,由于此方法简单易学,迅速收获了大批粉丝。

软件方法(下)分析和设计第8章分析 之 分析类图——知识篇Part01(202204更新)

图8-15 林正英电影海报

给软件开发人员一段文字描述,让他提炼和表达其中的领域概念和关系(通过ER图、类图……甚至口述表达都可以)。基于我在训练班上的体会,能在这个测试中给出合格结果的开发人员占全体开发人员的比例,如果在2000年占百分之x的话,二十多年后的今天,这个比例是否能占到千分之x都值得怀疑。

不过,形势已经在发生变化。

随着互联网的成熟,大部分组织都变成了“互联网组织”。以往以“互联网公司”著称的巨头们变成了行业领袖,宣称“我是做互联网的”已经不足以包装自己,必须要对领域深入挖掘了。

但是,开发人员“敏捷”惯了,怎么办呢?还能回得去吗?

软件方法(下)分析和设计第8章分析 之 分析类图——知识篇Part01(202204更新)

图8-16 分析技能下降之后,还能回得去吗?

于是,就出现了各种伪创新。

原创文章,作者:夜风博客,如若转载,请注明出处:https://www.homedt.net/77899.html