《软件方法(下)》第8章2023版8.1 分析工作流概述

news2024/10/5 12:57:35

DDD领域驱动设计批评文集

做强化自测题获得“软件方法建模师”称号

《软件方法》各章合集


第8章 分析 之 分析类图——知识篇

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

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

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

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

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

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

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

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

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

(1)识别类和属性

(2)审查类和属性

(3)识别类之间的泛化

(4)识别类之间的关联

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

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

这不符合实际工作中的情况。实际工作中,以上列出的几项工作看起来是交叉进行的。

我们在识别类和属性的过程中,可能会发现一些类有相同的属性,于是泛化出超类,可能会发现有的属性还可以在同一领域内分解,于是识别出另一个类以及两个类之间的关联……

注意我的用词:【看起来】交叉进行,因为更微小的过程依然遵守上述的推导过程(1)→(2)→(3)(4)。

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

8.1 分析工作流概述

8.1.1 知识的表达和组织

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

系统为了满足需求,必须封装一定的知识。这些知识不会乖乖地自己从知识的海洋中走出来,而是需要软件开发人员一点点识别出来,并有组织、有次序地把它们封装到系统中。

因此,我们要思考:

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

以及

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

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

不管是纯粹在大脑里面打转转,还是借助了纸笔或建模工具来协助,以上的思考都是逃不掉的。

如果需要封装的逻辑很简单,人脑的容量和运算速度能够胜任,在大脑里打转转还可以勉强应付,但是,能带来利润的系统都是复杂的(参见第1章),借助纸笔或建模工具来显式表达思考的过程很有必要,毕竟大脑容量和运算速度比一般人高出一个数量级的天才是很稀罕的。

注意区分“在大脑里完成”和“拍脑袋”的区别。

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

就像考试一样,面对一道有难度的填空题,考生可能有以下三种表现:

学霸:在大脑中迅速完成推导,直接在答卷填写结果。

普生:拿出草稿纸,规规矩矩推导,然后在答卷填写结果。

学渣:拍脑袋蒙一个,直接在答卷填写结果“敏捷试错”。

因为学霸的“在大脑中推导”和学渣的“拍脑袋”有一个容易观察到的共同点:没有草稿纸,所以学渣有时候会用这个共同点来假装自己是学霸,这一点要警惕。

8.1.2 核心域和非核心域

一个信息系统封装了若干领域的知识。其中,有一个领域的知识是该系统不能抛弃或替换的,这个领域称为"核心域",其他领域称为"非核心域"。

图8-1展示了不同系统类型的核心域和非核心域概念。

图片

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

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

还需要注意的是,核心域既可以是非计算机领域,也可以是计算机领域。例如,图8-1中的操作系统,其核心域概念有“处理器”、“内存”等。

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

有的开发人员在潜意识里用“懂”、“感兴趣”来划分"业务"和"技术":

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

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

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

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

有的开发人员在潜意识里则用“是否和计算机有关”来划分"业务"和"技术":

*和计算机无关→业务;

*和计算机有关→技术;

【说明1】

本书中的“核心域”和Eric Evans以及DDD(领域驱动设计)话语体系中的“核心域”(Core Domain)意思不同。

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

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

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

软件开发人员没有能力和责任做出商业竞争方面的判断。他要做的是,把涉众关于商业竞争方面的思考如实表达,并且用信息系统来封装其中适合封装的部分。

如果发现存在这样的“软件开发人员”,他有责任做出商业竞争方面的判断,那么说明这个人扮演了多个角色,既扮演涉众,也扮演软件开发人员。

【说明2】

另一个可以选择的用词是“问题域(Problem Domain)”,这也是之前大多数面向对象方法学的用语。但近年来,“问题域”和“解决方案域”等用语被领域驱动设计伪创新赶时髦胡乱使用,已经被严重污染。经过考虑,本书使用用语“核心域”和“非核心域”。

8.1.3 域之间映射和协作的套路

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

图片

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

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

图片

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

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

图片

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

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

person1=context.Persons.Find(ID)

注意,域和域之间的映射以及协作的套路,与域中的个体并不直接相关。

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

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

图片

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

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

如图8-6所示,假设三个域要考虑的知识点分别是a、b、c个,如果分开考虑,然后选择好域和域之间映射的套路,负担最小可以变成a+b+c;如果混在一起考虑,大脑的负担最大会达到a×b×c。只要a、b、c稍微大一些(都大于√3),相乘的结果就会大于相加的结果,随着a、b、c进一步增大,差距会迅速增大。

图片

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

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

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

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

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

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

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

8.1.4 被忽视的核心域

8.1.4.1 鸟类里充兽,兽类里充鸟

当今的软件开发现实,核心域受到的重视远远不够。

我们经常会看到这样的场景:

开发人员张三喜欢研究“底层”。明明他的本职工作是用C#编写生产管理系统,却不好好思考如何吃透核心域把项目做好,而是花时间研究编译器、操作系统甚至硬件。虽然工作是耽搁了,张三给人留下“勤奋好钻研”的印象。

甚至张三还可以写博客或公众号,在网络上成为媒体热爱的“网红程序员”。“网红程序员”很少探讨他当前所开发系统的复杂领域逻辑,更多是谈论某种语言或框架的特性。

张三这是在挑战难度,勇攀科学高峰吗?非也。

脱离自己真正要去解决的问题,热衷于“钻研(其实只是学习)底层”,这样的行为更像是偷懒而不是勤奋。所谓“底层”也只是另一个领域的知识,那个领域自有另外的人去研究。玩票式的“钻研”,在真正专注研究这个领域的研究者看来,实在是不值一提。

但是人性的弱点如此,正如钱钟书所说:“蝙蝠碰见鸟就充作鸟,碰见兽就充作兽。人比蝙蝠就聪明多了。他会把蝙蝠的方法反过来施用:在鸟类里偏要充兽,表示脚踏实地;在兽类里偏要充鸟,表示高超出世。向武人卖弄风雅,向文人装作英雄;”。

比起研究真正需要自己研究的底层(例如质管部和生产部之间的冲突、生产过程中的各种概念之间的关系……),研究(其实只是学习)MSIL的语法等等,要容易和愉快得多。

图片

图8-7 真正值得研究的“底层”

8.1.4.2 “技术”、“底层”没什么特别

开发一个基础设施领域的系统,例如操作系统,只需要关注计算机的资源,不需要关注客户、订单、库存、病历等具体某个应用领域的概念。也就是说,“负载”比较低。

另外,基础设施领域有大量已出版教材和先行例子,高校也为计算机和软件相关专业学生开设了相应课程(Linus Torvalds就是在大学教材中MINIX案例的激发下编写了Linux)。这样,开发人员的大脑比较容易把握基础设施领域的复杂性。

在2023年12月10日用"操作系统"为关键字搜索当当网(dangdang.com),得到315132件商品,按1/100来挤水分也有3000多件。其中,一步步教读者如何自己编写操作系统的书也不在少数。

图片

图片

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

市场已经对此做了回答。自己写(抄)一款操作系统并不难,但写一款能带来利润的操作系统太难了——这也同样适用于其他基础设施。

很多能够带来利润的应用系统,就没有基础设施那么好的待遇了。“自己动手写**应用系统”的书也不是没有,但其数量、深度和实用程度很难和基础设施的海量资料相比。

按道理,开发一个库存系统也应该可以不管基础设施,但遗憾的是,当前现实中大多数情况下还是要管。开发团队除了要懂得库存领域的知识,还需要懂MySQL、Apache、Linux……。也就是说,“负载”比较高。

媒体的关注度也不够。一名上班族早上起来,先上厕所用智能马桶冲PP,然后用智能电动牙刷刷牙,用微波炉热牛奶,边刷抖音边吃早餐,坐电梯下楼,开车去公司,用公司的业务系统工作。上面涉及到的七个智能系统中,估计只有抖音的开发人员可能会引起媒体的兴趣。

经常有"知名程序员"在谈到对建模(其实是ABC工作流)和UML的看法时,表示"对我来说不重要",进一步探究会发现,这些"知名程序员"往往开发的是基础设施领域的系统。或者也可以说,如果一名程序员开发的是"操作系统"、"编译器"、“浏览器”等带光环词汇的系统,更容易撩拨媒体从业人员的兴奋点,获得他们的青睐。

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


UMLChina公众号精选(20231208更新)按ABCD工作流分类

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1317597.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

手麻、腿麻、麻痛…背后竟隐藏7大疾病!多一个人知道,少一个悲剧!

手脚麻木背后的7大病症:骨病、脑梗、肿瘤…… 1、神经问题 上图四只手上橙色的区域代表了麻木感,如果您的手麻集中在无名指和小指的区域,您可以拿一张纸,用五个手指分别试着夹住,检验您的五个手指力量;您还…

软件测试之鲁棒性测试

文章目录 前言一、鲁棒性测试是什么?二、鲁棒性测试的目的三、测试原理3.1 错误数据处理3.2 异常情况处理 前言 Bootloader软件刷写鲁棒性(Robustness)测试是指对Bootloader软件进行连续多次的刷写测试,且一次Fail都没发生,以此验证Bootload…

MySql的增、删、改、查(MySql数据库学习——五)

增(数据添加/插入数据) 使用 INSERT INTO SQL 语句来插入数据。我们可以通过 mysql> 命令提示窗口中向数据表中插入数据,或者 通过PHP 脚本来插入数据。 sql语句: INSERT INTO table_name ( field1, field2,...fieldN ) …

系列九、事务

一、事务 1.1、概述 事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或者撤销操作请求,即:这些操作要么同时成功,要么同时失败。 例如: 张三给李四转账1000块钱&…

UI自动化Selenium 测试报告BeautifulReport使用及修改

一、BeautifulReport安装 pip安装 pip install BeautifulReport Pycharm中安装 二、原生报告样式 原生报告,因为我使用ddtunittest数据驱动模式,所以Excel中所有参数都会被拼接出来,导致测试方法里面有太多不需要展示的内容; …

IDEA配置一个新项目

git clone xxxxx 下载项目主分支 git checkout xxx 切换到需要开发的分支上 配置maven仓库 在File下的Settings中设置maven仓库 配置maven仓库的文件夹 配置好maven后,项目中会出现一个红色的pom.xml文件,右击文件,点击…,pom…

【计算机组成与体系结构Ⅱ】多处理器部分讨论题目

多处理机课堂讨论 1.并行计算体系结构有哪些? SIMD、MIMD 2.多处理机的存储结构有哪些? 对称式共享存储器结构、分布式共享存储结构 3.什么是多处理机的一致性? 如果对某个数据项的任何读操作均可得到其最新写入的值,则认为这个存储系统是一致的。 4.监听协议的工…

Ubuntu 常用命令之 ln 命令用法介绍

ln命令在Ubuntu系统中用于创建硬链接或符号链接。硬链接是指向文件的物理地址,而符号链接(也称为软链接)是指向文件路径的引用。 命令格式:ln [选项]... [-T] 目标(源文件) 链接(目标文件&…

windows下使用logstash同步跨网络集群的数据

我们在开发环境过程中,可能会遇到这样的场景。我们可以通过VPN访问远端的机房。有可能还要跨机房访问。这篇文章演示使用logstash,在windows上,去同步跨网络环境的不同机房之间的数据。 此方式受网络限制。适合同步小规模数据。 下载logstash…

基于Java SSM框架实现图书店仓库进销存管理系统项目【项目源码+论文说明】

基于java的SSM框架实现图书店仓库进销存管理系统演示 摘要 仓库作为储存货物的核心功能之一,在整个仓储中具有非常重要的作用,是社会物质生产的必要条件。良好的仓库布局环境能够对货物进入下一个环节前的质量起保证作用,能够为货物进入市场…

AVL树-详细解析【数据结构】

AVL树是首个被发明的自平衡二叉查找树,在1962年由两位苏联科学家G.M. Adelson-Velsky和E.M. Landis提出。AVL树得名于发明者的首字母。在AVL树中,任何节点的两个子树的高度最大差别为一,确保了树的平衡度,使得查找操作相比于普通的…

JavaScript数组分组groupBy

JavaScript 最近发布了一个方法 Object.groupBy,可以对可迭代对象中的元素进行分组。 语法: Object.groupBy(items, callbackFn)items 被分组的可迭代对象,如 Array。 callbackFn 对可迭代对象中的每个元素执行的函数。 举个例子&#…

圣诞树绘制合集-python绘制

使用Python绘制迷人的圣诞树 引言 随着圣诞节的临近,我们都希望以各种方式庆祝这个欢乐的节日。作为一名编程爱好者,你有没有想过用Python来创造节日的气氛呢?在这篇文章中,我将向你展示如何用Python绘制几种不同风格的圣诞树&a…

Linux之进程(四)(进程地址空间)

目录 一、程序地址空间 二、进程地址空间 1、概念 2、写时拷贝 3、为什么要有进程地址空间 四、总结 一、程序地址空间 我们先来看看下面这张图。这张图是我们在学习语言时就见到过的内存区域划分图。 下面我们在Linux下看一看内存区域是不是也是这么划分的。 可见在Li…

浅谈MapReduce

MapReduce是一个抽象的分布式计算模型,主要对键值对进行运算处理。用户需要提供两个自定义函数: map:用于接受输入,并生成中间键值对。reduce:接受map输出的中间键值对集合,进行sorting后进行合并和数据规…

RabbitMq交换机详解

目录 1.交换机类型2.Fanout交换机2.1.声明队列和交换机2.2.消息发送2.3.消息接收2.4.总结 3.Direct交换机3.1.声明队列和交换机3.2.消息接收3.3.消息发送3.4.总结 4.Topic交换机4.1.说明4.2.消息发送4.3.消息接收4.4.总结 5.Headers交换机5.1.说明5.2.消息发送5.3.消息接收5.4.…

Linux(23):Linux 核心编译与管理

编译前的任务:认识核心与取得核心原始码 Linux 其实指的是核心。这个【核心(kernel)】是整个操作系统的最底层,他负责了整个硬件的驱动,以及提供各种系统所需的核心功能,包括防火墙机制、是否支持 LVM 或 Quota 等文件系统等等&a…

数据分析为何要学统计学(2)——如何估计总体概率分布

明确总体的概率分布类型及参数是进行数据分析的基础,这项工作称为分布推断与参数估计。在总体分布及其参数不明确的情况下,我们可以利用手头掌握的样本来完成这项工作。具体过程由以下步骤组成。 第一步,样本统计特性直观估计 我们采用Seab…

虚拟机VirtualBox和VMware安装Ubuntu16配置静态IP

计算机集群安装之前,准备先在虚拟机上尝试一下,网上多是采用VMware虚拟机和CentOS系统,个人则准备采用已经安装好的VirtualBox虚拟机和Ubuntu16,但遇到第一个问题即是配置静态IP,那么对于以上两种虚拟机静态IP配置的问…

(数据结构)单链表的查找和长度计算

代码实现 #include<stdio.h> #include<stdlib.h> typedef struct LNode {int data;struct LNode* next; }LNode,*LinkList; //创建头结点 LNode* InitList(LinkList L) {L (LNode*)malloc(sizeof(LNode));if (L NULL){return NULL;}L->data 0;L->next N…