把设计模式用起来!(4) 用不好模式?之原理不明
(清华大学出版社 《把设计模式用起来》书稿试读)
上一篇:把设计模式用起来!(3)用不好模式?之时机不对
为什么用不好设计模式?——原理不明
难搞的顾客:“抹这种霜,真的能让我额头上的皱纹减少?”
销售:“当然啦,只要你坚持使用我们的淡纹霜,你脸的上细纹一定会比没有抹要少很多。”
难搞的顾客:“你们有没有现成的测试数据?提供三个对比组至少一年的数据,一组不抹,一组抹你家的,最后一组抹那瓶比你家便宜200元的霜。请您现在就把三组测试人员前后一年的皮肤状态数据,拿出来我要看下……”
销售:“这个……”
作为类比,再来看关于设计模式效果的对话,如下:
布道师:“设计模式能让代码更好的应对未来的变化……”
难搞的程序员:“不要和我说未来,设计模式被世人广泛认识都30年了,就没有现成的一些数据来证明它们的作用吗?”
布道师:“嗯,具体一点,您想看什么数据?”
难搞的程序员:“比如说,有人于2000年在某项目的一段代码里用了P模式,十年过后,该项目经历无数修改,依然存活。我想知道,当初代码中参与了P模式的那些代码,它们变化几何?会不会已经面目全非?”
布道师:“我明白你的意思了,一组应用了设计模式的代码,当然也是代码,所以你想观察号称可以‘更好应对变化’的设计模式自身,会不会反倒没能扛过变化?如果是,就能证明设计模式就是泥菩萨过河,自身难保,徒有虚名?”
难搞的程序员:“是的。”
布道师:“你的想法是错的。”
“难搞的程序员”想要的数据,倒是可以从不少论文中查到。我挑选了有延续关系的两篇,分别是2007年的《An empirical study on the evolution of design patterns(关于设计模式演化的一个实证研究)》和2012年的《How changes affect software entropy: An empirical study(变化如何影响软件熵值:一个实证研究)》。两篇论文的研究对象,均为知名的开源软件系统,它们是:
表 3 两篇论文所用到的开源软件项目
项目 | 关键特征 | 研究数据时间跨度 | 备注 |
JHotDraw | 中型项目/主要开发语言:Java | 3年 (2001~2004) | 仅论文1使用 |
ArgoUML | 中型项目/主要开发语言:Java | 论文1:5年 (2000 ~2005) 论文2:11年 (1998~2009) | |
Eclipse-JDT | 大型项目/主要开发语言:Java | 论文1:3年 (2001~2004) 论文2:10年 (2001~2011) | |
Mozilla | 大型项目/主要开发语言:C++ | 13年 (1998~2011) | 仅论文2使用 |
Samba | 中型项目/主要开发语言:C++ | 8年 (1996~2004) | 仅论文2使用 |
下面列举两篇论文涉及到设计模式与代码变更关系的一些实证结果:
- 在软件系统的演化进程中,尽管有些设计模式(指使用了设计模式的代码,下同)确实很少发生代码变更,但有些设计模式变更剧烈,甚至超过没有使用任何设计模式的代码;
- 确实有会一些设计模式比另一些模式,在不同的项目中都显得更惰性(变更少、代码稳定),反过来,有些模式明显比别的模式更善变(一有变更,首先动到的就是参与该模式的代码);
- 不同的设计模式产生频繁更改的代码位置也不尽相同,有的喜欢改动类的属性、有的喜欢改类的方法,有的改方法的实现,还有的最喜欢添加子类;
- 参与设计模式的代码的变更频率,以及它们所引发的其它代码的变更的数量,取决于模式自身,更多取决于模式所要实现的业务功能。越关键、重要的功能,变更通常越剧烈,相应的代码维护工作量也越大;
- 如果用错了设计模式,那么,后续发现问题加以订正时,变更往往更加剧烈(或许应该用“惨烈”来形容)——无论是参与设计模式实现的代码,还是涉及到的其它代码。
类似的实证结果还很多,并且都很有趣,也很值得大家找出这两篇论文细读加深思。不过,此刻我们只需要得出一个最简单的结论:应用了设计模式的代码,并不能“以不变应对万变”;相反,更多时候,设计模式是在“以变治变”。
举个论文中的实际例子,参与代理(Proxy)模式的代码,就容易被变更。《How changes affect software entropy: An empirical study(变化如何影响软件熵值:一个实证研究)》论文基于ArgoUML项目的研究结果表明:参与代理模式的类,比不参与任何设计模式的类具有更高的变化熵,也比参与复合、装饰器、工厂方法和观察者模式的类具有更高的变化熵,而与单例、状态策略和模板方法等模式的变化熵没有显著差异(In summary, results indicate that:Classes participating in Proxy design patterns have a higher change entropy than classes that do not participate in any design pattern, and than classes participating in Composite, Decorator, Factory Method, and Observer, while no significant difference can be found with Singleton, State Strategy, and Template Method. )。
为什么会这样呢?让我们简单分析一下。假设在项目中,PA代理A。如果A是一个无关紧要的功能,具体表现为,A功能做出来后无人问津,同时A对其它功能也没有多大影响……这就是前述的结果③。另一种可能,A功能非常重要,总有人用,总有人提建议或意见,于是它一直在改进,于是问题变成:为什么A一修改(包括增加特性),PA就大概率地也要随之更改?
答:因为在代理模式中,代理人不仅要对客户提供被代理人的功能,而且还要调整被代理人对外提供的功能。如果我们用C来表示客户,那么,C、PA、A三者关系如下:
PA的变化,可能来自A。当A兴冲冲地增加了一些新功能,或者气嘟嘟停用了某些功能,作为代理,PA 大概率需要同步这些变化。
PA的变化,也可能来自C。当C意欲增加新的请求时,PA必须同步增加对该项请求的处理。有意思的是,这时候A可能响应变化,也保持不动,后者的意思是:让PA先把这项请求应付下来。
如果变化仅仅来自两端,那还不能称代理人善变。PA的变化,极大可能来自自己,事实上这正是代理模式中的代理人最喜欢干的事:在C不知不觉和A不声不吭的状态下,为了某些特定需要,修改了某些请求,修改了某些响应,包括前面所说的:抛开A自行处理、响应了C的一些请求。在真实的项目世界中,存在大量A不太重要,但PA很重要,也就是“太监”比“皇帝”干的活多的情况。
PA只能代理A吗?不,现实世界中的项目,一个代理类需要代理多个被代理类的情况并不少见。
综上所述可知,参与代理模式的代码确实是善变的(除非被代理人和代理人都不重要)。我们真正关心的问题来了:以Proxy模式为例,一段善变的代码,是如何帮助其它代码更好地应对变化呢?我们让PA来说出答案,它是含泪说的:让C和A都守住单纯,你们那些奇奇怪怪的要求,冲我一个人来吧!
典型的牺牲我一个,幸福其他人。这就是Proxy模式发挥药效的基本原理。当然,并不是所有的模式都有这种思想高度,在本书后面的章节中我们将从源头上讲清楚每一种设计模式的发挥“药效”的原理。
现在就用一句话回答:命令(Command)模式在让代码更好地应对未的变化这件事上,原理是什么?也是通过牺牲自己吗?工厂方法(Factory Method)呢?观察者(Obsever)呢?如果你不能张口就来,那么,从对个别设计模式的认知,还没达到知根知底的水平。
不屈不挠的丁小明:“老师,我是没办法一提某个模式,就想到它的作用原理,但我并不认为我用的设计模式都错了。”
“发烧吃退烧药,流鼻涕就吃感冒药,咳嗽厉害,喝上半个月的止咳糖浆……”
“对啊,烧退了,感冒症状消失了,咳嗽也停了,这样不行吗?”
“当病人可以,当医生不行。”
相关文章:

把设计模式用起来!(4) 用不好模式?之原理不明
(清华大学出版社 《把设计模式用起来》书稿试读) 上一篇:把设计模式用起来!(3)用不好模式?之时机不对 为什么用不好设计模式?——原理不明 难搞的顾客:“抹这种霜&#…...

安卓13去掉下拉菜单的Dump SysUI 堆的选项 android13删除Dump SysUI 堆
总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析3.1 位置13.2 位置24.代码修改5.编译6.彩蛋1.前言 客户需要去掉下拉菜单里面的Dump SysUI 堆图标,不让使用这个功能。 2.问题分析 android的下拉菜单在systemui里面,这里我们只需要定位到对应的添加代…...
vue3常用的组件间通信
一 props props 可以实现父子组件通信,props数据是只读。 1. 基本用法 在父组件中,你可以这样传递 props: <template><ChildComponent message"Hello, Vue 3!" /> </template><script setup> import C…...
Windows 查找特定进程的ID并杀死
"*分析用户信息.py*" 换为自己的文件名 Get-WmiObject Win32_Process | Where-Object { $_.CommandLine -like "*分析用户信息.py*" } 查找后 内容如下 __GENUS : 2 __CLASS : Win32_Process __SUPERCLASS …...

Snapchat API 访问:Objective-C 实现示例
Snapchat 是一个流行的社交媒体平台,它允许用户发送和接收短暂存在的图片和视频。对于开发者来说,访问 Snapchat API 可以为应用程序添加独特的社交功能。本文将介绍如何在 Objective-C 中实现对 Snapchat API 的访问,并提供一个详细的代码示…...

ps证件照蓝底换白底
ps证件照蓝底换白底 1、打开 Photoshop,导入需要处理的照片。 2、左侧工具栏中选择“魔棒工具”,点击证件照的背景区域进行选择。 3、使用快捷键 Shift F5 或者从顶部菜单选择“编辑” -> “填充”,在弹出的对话框中选择“填充内容”中…...

阿里云kafka消息写入topic失败
1. 问题现象描述 20240918,14:22,测试反馈说kafka有问题,生产者写入消息的时候报错,并发了一张日志截图,主要报错如下: to topic xxxx: org.apache.kafka.common.errors.TimeoutException: Expiring 1 record(s) for x…...

图像放大效果示例【JavaScript】
实现效果: 当鼠标悬停在小图(缩略图)上时,大图(预览图)会随之更新为相应的小图,并高亮当前悬浮的小图的父元素。 代码: 1. HTML部分 <!DOCTYPE html> <html lang"z…...
【C#生态园】云端之C#:全面解析6种云服务提供商的SDK
C#开发者必读:深度比较6种云服务SDK 前言 随着云计算技术的迅猛发展,越来越多的企业和开发者选择将应用程序部署到公共云平台上。针对C#开发者而言,各大云服务提供商纷纷推出了适用于C#的SDK,以便开发者能够更轻松地与其云服务进…...

远程升级又双叒叕失败?背后原因竟然是。。。
最近又遇到了远程升级接连失败的情况,耐心和信心都备受折磨! 事情是这样的:有客户反馈在乡村里频繁出现掉线的情况,不敢耽搁,赶紧联系小伙伴排查测试,最后发现,只有去年某一批模块在当下环境才…...

【测试】——Selenium API (万字详解)
📖 前言:本文详细介绍了如何利用Selenium进行Web自动化测试,包括定位元素(如cssSelector和xpath)、常用操作函数(如点击、输入等)、窗口管理、键盘鼠标事件和浏览器导航,以及处理弹窗…...

Redis:原理+项目实战——Redis实战3(Redis缓存最佳实践(问题解析+高级实现))
👨🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习 🌌上期文章:Redis:原理项目实战——Redis实战2(Redis实现短信登录(原理剖析代码优化)&#x…...

刚刚,Stable Diffusion 2024升级,最强Ai绘画整合包、部署教程(解压即用)
2024Ai技术大爆发的元年 目前两款Ai神器大火 一款是大名鼎鼎的Chat GPT 另外一款—Stable Diffusion 堪称全球最强Ai绘画工具 Stable Diffusion Ai绘画2024版本更新啦! 从4.8.7更新至**4.9版本!**更新优化和大模型增加,无需安装…...

【AIGC】ChatGPT提示词助力高效文献处理、公文撰写、会议纪要与视频总结
博客主页: [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 💯前言💯高效英文文献阅读提示词使用方法 💯高效公文写作提示词使用方法 💯高效会议纪要提示词使用方法 💯高效视频内容分析提示词使用方法 &a…...
centos7更换国内下载源
📖centos7更换国内下载源 在CentOS 7上更换为国内源可以通过替换 /etc/yum.repos.d/CentOS-Base.repo文件来实现。以下是一些常用的国内源以及如何更换的示例: 阿里云源: mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Bas…...

【Linux】常用指令【更详细,带实操】
Linux全套讲解系列,参考视频-B站韩顺平,本文的讲解更为详细 目录 一、文件目录指令 1、cd【change directory】指令 2、mkdir【make dir..】指令 3、cp【copy】指令 4、rm【remove】指令 5、mv【move】指令 6、cat指令和more指令 7、less和…...
力扣3290.最高乘法得分
力扣3290.最高乘法得分 递归 记忆化搜索 对于b数组,从右往左考虑取不取,如果取则问题变成b[0] ~ b[i-1]间找j - 1个数 如果不取,则问题变成b[0] ~ b[i]间找j个数即dfs(i,j) max(dfs(i-1,j) , dfs(i-1,j-1) a[j] * b[i]) 边界:…...

Python | Leetcode Python题解之第413题等差数列划分
题目: 题解: class Solution:def numberOfArithmeticSlices(self, nums: List[int]) -> int:n len(nums)if n 1:return 0d, t nums[0] - nums[1], 0ans 0# 因为等差数列的长度至少为 3,所以可以从 i2 开始枚举for i in range(2, n):i…...
深入理解 ClickHouse 的性能调优与最佳实践
1. 介绍 ClickHouse 是一款由 Yandex 开发的开源列式数据库,专为在线分析处理(OLAP)场景设计。它以极高的查询性能著称,尤其适用于大规模数据的快速聚合和分析。自发布以来,ClickHouse 在多个行业中得到了广泛应用&am…...

Elasticsearch——介绍、安装与初步使用
目录 1.初识 Elasticsearch1.1.了解 ES1.1.1.Elasticsearch 的作用1.1.2.ELK技术栈1.1.3.Elasticsearch 和 Lucene1.1.4.为什么不是其他搜索技术?1.1.5.总结 1.2.倒排索引1.2.1.正向索引1.2.2.倒排索引1.2.3.正向和倒排 1.3.Elasticsearch 的一些概念1.3.1.文档和字…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...

Axure 下拉框联动
实现选省、选完省之后选对应省份下的市区...

PydanticAI快速入门示例
参考链接:https://ai.pydantic.dev/#why-use-pydanticai 示例代码 from pydantic_ai import Agent from pydantic_ai.models.openai import OpenAIModel from pydantic_ai.providers.openai import OpenAIProvider# 配置使用阿里云通义千问模型 model OpenAIMode…...