续1-续3《你的医书是假的!批评付施威的《DDD诊所——聚合过大综合症》

news2024/11/25 23:27:27

DDD领域驱动设计批评文集

“软件方法建模师”不再考查基础题

《软件方法》各章合集

我写了一篇文章,批评付施威的《DDD诊所——聚合过大综合症》(以下简称《DDD诊所》),文章是《你的医书是假的!批评付施威的《DDD诊所——聚合过大综合症》》(以下简称《你的医书是假的》)。

一、《你的医书是假的》关键内容

《DDD》诊所说,下面这个是不变式:

图片

图1 《DDD诊所》截图

我在《你的医书是假的》中提出以下批评:

(1)不变式是类的不变式。《DDD诊所》作者把它放在关系上是不对的,说明该作者没有理解不变式。

(2)《DDD诊所》作者引用的《领域驱动设计》译文的翻译很糟糕(参见《猴子掰玉米?比较不同版《领域驱动设计》说“不变式”和“聚合”》),关键内容的意思译反了,而《DDD诊所》作者并无察觉,反而整段引用作为依据。

(3)不变式不是用来描述像图中的“订单总价=sum(订单行总价)”这样的内容的。如果这样的约束被严格遵守,说明“订单”的“总价”属性在逻辑上是冗余的,应该删掉。既然“总价”属性没了,图1的所谓“不变式”自然也就没有了。

也就是说,改变订单属性值(包括加/删订单行以及其他操作)的时候,不需要判断某个含有“总价”属性的表达式是否为真——实际上也没法判断,“总价”属性都没有了嘛。

接下来,我再针对图1做进一步的阐述,帮助进一步理解不变式,并评点在《你的医书是假的》文章下面的留言。

二、等号和赋值号没搞混吧?

不变式是一个布尔表达式。

我在这里提这一点,到底是什么意思呢?

意思是:

如果发现有“不变式”表示为相等关系的表达式,那就要警惕一下,建模人员是不是把“=”当成赋值来理解了。

例如,图1的式子“订单总价=sum(订单行总价)”,有可能就会被误解成“把订单项的价钱之和赋值给订单总价以同步这两边的值”。

读者可以注意到,我在《你的医书是假的》画的图例中,没有相等关系的不变式。

读者还可以对比下面的例子: 

图片

图2 三角形及其不变式

这个还行,对吧?

那读者再看这个:

图片

图3 错例:等边三角形及其不变式?

这个是不是比较诡异?不过,这样的图倒是符合DDD伪创新的口味:投资少、见效快、门槛低、产量高、仪式感十足。

注意,我在图3中用“==”表示相等关系,而不是用“=”。其实这是违反OCL语法的,OCL里就是“=”表示相等关系,和SQL类似,但考虑到上面说的赋值误解(特别是显示在类图上时),我倒是觉得OCL应该改一下。

那么,可不可以有相等关系的不变式?可以有,但很少。相等关系表达式,经常是出现在前置条件和后置条件,而不是不变式。

什么时候可以有相等关系的不变式,这个就涉及到相当多的知识,不是本文能装得下的了,感兴趣的读者可以关注我的《软件方法》。

三、“订单总价=sum(订单行总价)”的另一个问题

这个问题在《你的医书是假的》中没指出来,在这里补上:

“订单总价=sum(订单行总价)”这个所谓的“不变式”,和图1中的属性名、关联角色名对不上。

图1中的属性名、关联角色名没有“订单总价”和“订单行总价”,只有“订单.总价”、“订单行.总价”——这也说明《DDD诊所》作者当时没有理解不变式是干什么用的。

四、不变式在哪里实现?

上面说了,不变式是一个布尔表达式,它只是说,对象的属性值不能违反这个约束,至于如何保证这一点,它没有做任何指定或暗示。

例如,把每一个不变式封装成一个方法,如果某个操作会修改某不变式中所涉及到属性的值,就调用相应方法来检查——这个工作可不轻松。

如果所用的实现技术支持前置条件、后置条件、不变式等契约编程,例如.NET中提供的Contract类,使用该类提供的服务即可。

五、类似“sum(订单行总价)”的运算会出现在哪里?

可能会有人问这样的问题。

类似“sum(订单行总价)”的运算,像其他针对各个属性的各种运算一样,并没有什么特别,会出现在类的某个操作或多个操作里。

什么操作?正确的回答是“信息不足,不知道”,可能“订单”专门有一个“计算总价”的操作,“sum(订单行总价)”藏在该操作的实现中,也可能“订单”根本不需要有“计算总价”的操作,“sum(订单行总价)”藏在“订单”的某个更复杂的操作的实现中。

不管怎么样,这和图1那个相等关系的“不变式”不存在什么有规律的对应关系。

六、关于付施威的解释

《DDD诊所》作者付施威在《你的医书是假的》的评论区留言解释:

图片

图4 《DDD诊所》作者付施威的解释

先说解释中的第3点:

如果还有“订单总价<3000”这样的约束,之前的结论也没有变化,只不过“订单”的不变式多了一个: 

图片

图5 加上订单总价<3000后的类图变化(注:我把类名和属性名改成订单项.价钱)

再说解释中的第1点:

付施威谦虚地解释“DDD诊所是内部活动”、“主要职责是被人研究”,但下面的海报传递出来的信息似乎是不一样的。 

图片

图6 活动海报1

图片

图7 活动海报2

开直播以专家身份传道授业解惑没问题,但被指出问题了,又谦虚地说是学习体会,这个似乎就不太好。

七、关于林宁的评论

付施威的同事林宁在《你的医书是假的》下留言:

图片

图8 林宁在《你的医书是假的》评论区的留言

以下的观点并非只针对林宁的评论,而是针对近年DDD圈子的典型反馈——“潘老师说得对,但是%……&¥&**”。

并没有恶语相向,而是潘老师说的没问题,我们也没问题,大家都Happy,这不是挺好吗?

我在《潘老师,你的气度有点小了!》阐述了一些我的看法,本文不再涉及该文中提到的内容。

本文重点展开谈一谈林宁所说的“一定程度的冗余”。

(1)问题①②③

问题①

假设只考虑领域逻辑,不考虑其他问题。我们整理包括需求规约在内的各种各样的素材,(使用面向对象方法)提炼出系统需要封装的各种领域知识,并整理成类模型。模型中,根据不同的变化频率,各种概念分别放置在类、属性、关系等合适的地方,没有冗余。

(可以理解成程序运行在一个存储空间无限大,通信和运算速度无限快且分布在全宇宙的计算机上,不用考虑什么硬盘、内存、Cache、加载,万事俱备,只欠你所分析的领域知识。)

为了方便后面的叙述,我把这称为问题①——这是最难也最想逃避的。

我依然用之前的订单例子,图9左下角的类模型,只涉及目标系统需要封装的领域概念和逻辑,不涉及任何具体实现,我们把它叫作分析模型。

(图9左下角的类模型没有行为部分。如果需要充分表达行为逻辑,可以(使用面向对象方法)建立类的状态机模型,把类的各种行为逻辑分别放在状态机的触发器、迁移动作,状态内动作等合适的地方,同样,没有冗余。)

图片

图9 问题①②③

问题②

现实中的计算机和网络,运行空间、运行速度、通信速度都是有限制的。

如果人们对性能的要求没那么敏感,当前的计算资源又允许,那么实现模型(代码、存储……)和图9左下角的分析模型之间映射非常直接。

假设出现这样的情况:需要频繁地获得订单的总价,而频繁计算订单总价导致出现了不可调和的性能问题,解决方案之一是在实现模型中给“订单”添加一个冗余属性(用数据库语言就是冗余字段)“总价”,这就是《DDD诊所》的情况。图9右上角就是一个C#实现的“订单”类,“总价”前面有个“/”,说明这个属性是派生的(derived)。

为了方便后面的叙述,我把这称为问题②。

*可能有读者会想:这样的话,《DDD诊所》那个“订单总价=sum(订单行总价)”(前文已说过,其实应该是“总价==sum(…订单行.总价.…)”)的不变式不就有了吗?依然没有,领域逻辑并没有变化,分析模型不需要改动。这种添加冗余以及同步的套路不属于领域逻辑,实际上和具体的领域订单、物流、制造、社交……是正交的——这也是常见的批量刷废话套路,把两个正交的东西混在一起。

问题③

假设考虑到开发团队的结构,把系统分成多个“微服务”,分由各个小团队应用各自的技术栈独立完成。这时,可能会在多个“微服务”中有“订单”类。这应该就是林宁说的“拓展系统和团队”。

为了方便后面的叙述,我把这称为问题③,见图9右下角。

*注意:本文列出的问题①②③是对应《DDD诊所》的内容以及林宁的评论内容,不代表只存在这么几个问题。

(2)问题②③的出现对问题①有帮助吗?

没有帮助。

如果问题②③的出现有助于解决问题①,我们早就可以这样干了,不用等到真的有问题②③出现,假想它们存在就行了。

建模人员正在抓耳挠腮地思考和整理各种领域逻辑(问题①),太难了!

有办法。给原问题叠加一个性能问题“10亿并发用户”(问题②),再叠加一个团队问题“100个团队并行开发”(问题③),果然,问题①迎刃而解。

可能会有人提到类似下面这个:

Scott Millett等的“Patterns, Principles, and Practices of Domain-Driven Design”书中举了一个例子,一个庞大的Product类怎样被分解成若干个上下文中的小Product类:

图片

图10 “Patterns, Principles, and Practices of Domain-Driven Design”中的例子

张逸所著的《解构领域驱动设计》中,也用了类似的例子:

图片

图11 《解构领域驱动设计》中的例子

这看起来不就是问题③有助于问题①的解决吗?

其实这是问题①没做好。稍微有建模能力的人,谁会把这么一大堆不同变化频率的知识混到一起?

我写过一篇《发现在写代码过程中对需求的认识更清晰了》谈过类似问题,供参考。

图片

图12 《发现在写代码过程中对需求的认识更清晰了》截图

(3)提防伪创新混水摸鱼

李三正在抓耳挠腮地思考问题①,太难了!

这时候,“创新专家”张四过来推销一种“新方法学”,大意是:环境和时代变了,你需要问题②③。

于是,李三抛开问题①不谈,先搞问题②③,工作量一下就饱满了,看着李三忙忙碌碌,李三的老板也开心。

时间过去,也许问题①没解决,但没关系,问题②③那边没有功劳也有苦劳。

时间过去,也许在忙问题②③的同时,如图12所说,懵懵懂懂,问题①也解决了一些。那就刚好证明张四的“新方法学”有助于解决问题①。

总之,问题②③的引入提供了一层遮羞布,掩盖了李三在问题①上的能力不足问题。

于是,李三觉得张四的新方法学“十分受用”。

(4)可以理解和不能忍

我拓展一下《软件方法》第1章的大楼类比:

两座大楼耸立在那里,要判断地震来了哪座大楼不容易塌,要考虑的是大楼的结构、所用的材料、所在位置的地质环境等,和这座楼是哪家公司建造的,要了多少钱,建造大楼的公司内部是怎样的组织结构,甚至大楼是猫建造的、狗建造的、外星人建造的,没有直接关系。

但要研究这些让楼不容易塌的直接影响因素,涉及到艰深的工程力学、流体力学、岩土力学等知识。架构师李三没把这些知识学扎实,正在那里犯愁呢。

这时,张四出现了。张四说,时代变了,现在盖楼要讲“新建筑学”,要考虑到人际关系,要搞好团结。

于是,李三想着反正“老的”工程力学那些我也搞不懂,还是搞“人”轻松一些。这样吧,有几个包工队跟自己混,就分几个包,大家开干就是。

转换思想后,李三每天累并快乐着,灯红酒绿,推杯换盏。

而且,运气好的时候,盖出来大楼确实也能住人。

**********

如果李三说,公司又不是我的,想那么多干什么,这可以理解;

如果李三说,这么干盖楼快,反正老板要的就是在某某大日子到来之前有个样子货交差,这也可以理解;

如果李三说,这么干有利于建筑团队的安定团结(虽然坑顾客),这也可以理解;

但如果李三和张四说,“新建筑学”盖出来的大楼更抗震,甚至到清华大学建筑学院开课“划时代革命性的工程力学”,取代原有的“工程力学”——这就不能忍了!

(5)最后说一个文风问题

短短一句话,就有“让网变成树”、“切成服务”、“拓展系统和团队”三个可以作为“创新大会”演讲标题的高大上用语。

图片

图片

图片

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

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

相关文章

【JAVA】什么是异常

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;浅谈Java &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 异常 1. 什么是异常1.1 概念1.2 异常的体…

第五章 树与二叉树 三、二叉树的先、中、后序遍历

一、定义 树的遍历是按照一定的顺序访问树的所有节点&#xff0c;常用的遍历方式有三种&#xff1a;先序遍历、中序遍历和后序遍历。 先序遍历&#xff1a;从根节点开始&#xff0c;按照根节点-左子树-右子树的顺序遍历整棵树&#xff0c;即先访问根节点&#xff0c;然后遍历左…

找不到mfc100u.dll,无法继续执行如何修复呢?分享三个解决方法

我想和大家分享一个常见的电脑问题&#xff1a;找不到mfc100u.dll,无法继续执行。这个问题可能会给我们带来很多不便&#xff0c;但是请不要担心&#xff0c;我将为大家介绍五个解决方法&#xff0c;帮助大家轻松修复这个问题。 mfc100u.dll是Microsoft Foundation Class (MFC)…

Python爬虫实战案例——第三例

文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff01;严禁将文中内容用于任何商业与非法用途&#xff0c;由此产生的一切后果与作者无关。若有侵权&#xff0c;请联系删除。 起点中文网月票榜加密字体处理 字体加密的原理&#xff1a;就是将一种特定的…

8.28 菱形继承 虚继承 多态 模板

#include <iostream>using namespace std;class Animal { public:Animal() {cout << "111" <<endl;}virtual void perform() 0; };class Dol:public Animal { public:Dol() {}void perform(){cout << "海豚" << endl;} };c…

人须在事上磨 方才立得住|「云上百世慧」锐评

巨大的用户群体绝不仅意味着金山银山&#xff0c;还意味着责任如山。——《人民日报》 云上百世慧&#xff0c;智能制造专题研讨会顺利开展&#xff0c;吸引了行业内的不少人才前来参会。 云上百世慧举办三期&#xff0c;共吸引行业人员400余名&#xff0c;参会企业90余家。 …

无涯教程-Python机器学习 - Stochastic Gradient Boosting函数

它也称为梯度提升机。在下面的Python食谱中,我们将通过使用pima Indians糖尿病数据集上的 sklearn 的 GradientBoostingClassifier 类来创建随机梯度Boostingensemble模型进行分类。 首先,导入所需的软件包,如下所示: from pandas import read_csv from sklearn.model_select…

从零开始探索C语言(二)----变量、常量和存储类

文章目录 1. C 变量1.1 C 中的变量定义1.2 变量初始化1.3 变量不初始化1.4 C 中的变量声明1.5 C 中的左值和右值 2. C 常量2.1 整数常量2.2 浮点常量2.3 字符常量2.4 字符串常量2.5 定义常量2.6 #define 与 const 区别 3. C 存储类3.1 auto 存储类3.2 register 存储类3.3 stati…

checkstyle检查Java编码样式:javadoc注释检查

说明 checkstyle可以检查javadoc注释是否符合规范。 Javadoc注释以/**开头&#xff0c;以 */结尾&#xff0c;可以被javadoc等工具提取&#xff0c;形式如&#xff1a; /*** 保存了一些常数.* author thb**/Javadoc注释的首行以句号&#xff08;.&#xff09;、问号&#xf…

博客写长篇,公众号写短篇

博客使用的markdown格式非常适合技术类的文章&#xff0c;我大部分博客的内容写的都很长&#xff0c;有一部分很深的内容&#xff0c;也有特别基础的内容。 因为之前写博客总会花费太多时间&#xff0c;所以量比较少&#xff0c;现在打算用更少的时间在公众号写一些简单的内容…

富文本base64字符串转file文件上传

1.富文本以html字符串格式存储 2.以图片为例&#xff0c;获取img base64字符串,转为file文件 let content "" const regEx /(?<(img src"))[^"]*?(?")/gims; let imgs content.match(regEx); let fileList []; for (let i …

8.28day48

198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 知识点&#xff1a;动规五部 1.dp数组的含义&#xff1a;dp数组表示打劫改房间的最大收益 2.递推公式&#xff1a;打劫i家&#xff1a;从后往前推 我们如果打劫第i家 那么第i-1家绝对不可能背打劫 所以&#xff1a;dp…

跟这几个全日制专业相比,MBA/MEM/MPA的难度只能算小弟弟~

每年很多在职考生会因为MBA联考的难度而选择放弃&#xff0c;但实际上管理类硕士的初试难度目前来说应该是国内所有双证硕士考试中最容易的类别之一了。就从每年几个在职类专业的国家线也看得出来总体竞争并未达到十分惨的地步&#xff1a; 客观来说&#xff0c;300分总分…

免费高清图片素材库,我推荐这6个~赶紧收藏!

找高清图片素材就上这6个网站&#xff0c;我强推。免费、付费、商用的素材都能找到&#xff0c;赶紧先收藏起来吧&#xff01; 菜鸟图库 美女图片|手机壁纸|风景图片大全|高清图片素材下载网 - 菜鸟图库 网站主要为新手设计师提供免费素材&#xff0c;这些素材的质量都很高&a…

【JS真好玩】自动打字机效果

目录 一、前言二、布局分析三、总体样式四、中间部分五、底部5.1 div5.2 label5.3 input 六、JS让它动起来6.1定时器6.2 字符串处理6.2.1 slice6.2.2 splice6.3.3 split 七、总结 一、前言 大家好&#xff0c;今天实现一个自动打字机效果&#xff0c;旨在实现一些网上很小的de…

【C++题解】[广州大学附属中学-AKCSP2022信心联考]数组树

P a r t Part Part 1 1 1 读题 题目描述 w g y wgy wgy有 n n n个很不错的数&#xff0c;第 i i i个数是 a i a_i ai​&#xff0c;他想用它们组一棵数树送给 a y b ayb ayb。 a y b ayb ayb对一棵数树的喜爱程度是这棵数树的每个子树包含的数的和的总和。简单来说&#xff0…

【数据结构与算法系列1】 二分法查找

给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 示例 1: 输入: nums [-1,0,3,5,9,12], target 9 输出: 4 解释: 9 出现…

软件与软件工程

软件 软件的概念以及特点&#xff1a; 软件是计算机系统中不可或缺的一部分&#xff0c;与硬件共同构成特定的系统功能。 人们通常把各种不同功能的程序&#xff0c;包括系统程序、应用程序、用户自己编写的程序等称为软件 软件的概念: 软件不仅包括程序&#xff0c;还包括程序…

Linux环境下的内存管理(2/7)

要想深入理解内存中的堆栈管理机制&#xff0c;孤立地分析并不是一个好方法&#xff0c;因为堆栈内存不是仅靠程序本身来维护的&#xff0c;而是由操作系统、编译器、CPU、物理内存相互配合实现的。 在 Linux 环境下运行的程序&#xff0c;在编译时链接的起始地址都是相同的&a…

vue左侧漏斗切换 echart图表动态更新

这个需求是根据点击左侧的箭头部分&#xff0c;右侧图表切换&#xff0c;左侧选中数据高亮&#xff08;图片用的svg&#xff09; 一、效果图 二、vue组件 <template><div class"funnel_wrap"><div class"flex_between"><div class&q…