《软件方法(下)》8.3.2.2 警惕拼凑泛化(202405更新)

embedded/2024/9/23 20:49:01/

DDD领域驱动设计批评文集

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

《软件方法》各章合集


8.3 建模步骤C-2 识别类的关系

8.3.2 识别泛化关系

8.3.2.1 识别泛化的思路

(3)自上而下(从一般到特殊)

如图8-92所示,这个识别思路就是8.2.5.6 属性是否对所有对象都有意义里的思路。

图片

图8-92 自上而下-一个类分裂出子类

和上面的“自下而上”一样,这里的属性也包括关联,如图8-93。

图片

图8-93 关联只对部分对象有意义时分出子类

不一定像图8-93那样关联到不同的类。关联到同一个类但角色名不同,而某个角色只对部分对象有意义,也可以考虑分出子类,如图8-94。

图8-94 某个角色只对部分对象有意义时分出子类

8.3.2.2 警惕拼凑泛化

您可能注意到,以上我们尽量通过属性(包括关联)来解释泛化关系。

虽然泛化带来的好处是落在行为上,但如果抛开属性直扑行为,很可能会带来“伪泛化”、“伪面向对象”。

如图8-95,因为X和Y都有操作op1,所以泛化出A,把op1提上去成为抽象操作。这个没有问题。

问题出在前面,怎么知道op1作为X和Y的操作是合适的?最终的依据还是X和Y的属性(包括关联)。

图8-95 操作怎么来的,需要有依据

有一种偷懒遮羞布,就是胡乱安排操作,然后拼凑出泛化关系,根本不顾安排的操作是否合理。

一些伪面向对象实践经常得到各种名称的最后是or或er(汉语则为“器”)的类。

开发人员可能一开始按照面向过程的思路噼里啪啦把代码写出来,然后出于赶时髦需要“面向对象”,他就把过程名称加上or或er作为类名称,然后把原来的过程作为or或er类的操作。

当存在多个类似过程时,还可以加上一些泛化关系(或接口-实现)来做点缀,如图8-96,这样看起来就更有“面向对象”的味道了。

图片

图8-96 带有泛化关系的or或er类

可能在某些开发人员眼里,图8-96很有格调,可以用来吹嘘的高大上词汇有:OCP(开放-关闭原则)、DIP(依赖倒置原则)、模板方法模式……等。这些由泛化关系衍生出来,被网红圈子广泛吹嘘的原则和模式作用十分有限,指望了解被包装出来的“SOLID原则”之类就能应对软件复杂性,那真是太天真了。

★关于Robert C. Martin和他的书《敏捷软件开发-原则、方法与实践》的评价,参见本书2024版第1章关于“敏捷”染色的阐述。

or或er类往往没有属性,只有操作,大量的逻辑仍然隐藏在子类操作的实现中。当然,开发人员也可能觉得这是好事,“这说明我有算法啊!”,好像很高大上,但这所谓的“算法”(其实是核心域逻辑)正是建模的重点,结果被开发人员完美地遮掩过去了。

和其他的偷懒遮羞布类似,这种or或er的做法一一对应,思考工作量小,还有各种高大上词汇护法,于是开发人员洋洋得意,感觉自己已经很厉害了,连称“受用”,纷纷去拥抱这种偷懒遮羞布。

or或er类有时会使用“策略模式”作为伪装,如图8-97,哇,我可以灵活组装各种策略!顺便再吹一通“组合优于继承”之类,其实还是换汤不换药,核心域逻辑仍然隐藏在子类操作的实现中。那些“策略”才是建模的重点,同样被开发人员完美地遮掩过去了。

图片

图8-97 “策略模式”换汤不换药

如果一个类的命名中有类似这些内容:er、or、器、策略、Strategy、Policy、规则、Rule、算法、Algorithm……然后这个类或其子类的操作中有长长的“算法”,那么应该思考一下,长长的“算法”中到底定义了哪些变量?哪个部分的代码最复杂?

背后往往就是候选的实体类以及需要封装的操作,尽量分离出实体类,把各种逻辑尽量封装在实体类的操作中。这样的思考更辛苦,但也更有价值。

有心的读者可以仔细观察一下你接触到的领域驱动设计相关的文章,看看有没有这样的现象:

全篇文章没有剖析复杂一点的逻辑,但会有“**策略”、“**规则”这样的类或者组件存在。莫非这些“**策略”、“**规则”是别人已经做好的,他拿来就用?追问下去,多半不是,而是他要负责解决的问题。

你猜我怎么知道是这样?因为我接触的开发团队和开发人员太多了,追问下去,脓包破裂的概率极高。


http://www.ppmy.cn/embedded/39222.html

相关文章

Android 按钮Button点击音效

一、新建工程 编译运行&#xff0c;确保工程无误&#xff0c;这里不过多赘述。 二、UI布局 添加两个播放音效Button <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"…

Typescript基础

文章目录 基本类型编译选项TypeScript打包webpack整合Babel 面向对象定义类构造函数封装属性存取器静态属性继承重写抽象类&#xff08;abstract class&#xff09; 接口&#xff08;Interface&#xff09;泛型泛型函数泛型类泛型继承 基本类型 声明 类型声明是TS非常重要的一…

Leetcode—138. 随机链表的复制【中等】(cend函数)

2024每日刷题&#xff08;129&#xff09; Leetcode—138. 随机链表的复制 实现代码 /* // Definition for a Node. class Node { public:int val;Node* next;Node* random;Node(int _val) {val _val;next NULL;random NULL;} }; */class Solution { public:Node* copyRan…

Web开发小知识点(一)

1.input不支持自动换行解决办法 input不支持换行&#xff1b; textarea&#xff1a;支持换行&#xff1b; 设置提示文案的css 用&#xff1a;&#xff1a;placeHolder 2.textarea禁止拖拽、去掉右下角三角&#xff08;css下&#xff09; textarea{resize:none; } 3.用户对访…

Oracle-一次TX行锁堵塞事件

问题背景&#xff1a; 接用户问题报障&#xff0c;应用服务出现大量会话堆积现象&#xff0c;数据库锁堵塞严重&#xff0c;需要协助进行问题定位和排除。 问题分析&#xff1a; 登录到数据库服务器上&#xff0c;首先查看一下数据库当前的等待事件情况&#xff0c;通过gv$ses…

STC8增强型单片机开发【定时器Timer⭐】

目录 一、引言 二、定时器基础知识 三、STC8定时器配置 四、代码示例 五、总结 一、引言 在单片机开发中&#xff0c;定时器&#xff08;Timer&#xff09;是一个极其重要的组件&#xff0c;它允许开发者基于时间触发各种事件或任务。STC8增强型单片机作为一款功能丰富的…

Java引入第三方库JLaTeXMath处理Latex特殊字符的公式

本文主要是为了处理大模型返回输出的Latex 结构的特殊字符 展示为熟知的正常公式 例如&#xff1a;我输入一条Latex公式 然后输出成正常的可见公式 $\frac{990}{500} \frac{99}{50} \frac{29.7}{25} \frac{29.7}{27} \approx \frac{2}{27}$ 1. pom.xml文件中引入JLaTeXM…

基于SSM的文化遗产的保护与旅游开发系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的文化遗产的保护与旅游开发系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;…