当前位置: 首页 > news >正文

把设计模式用起来!(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 可以实现父子组件通信&#xff0c;props数据是只读。 1. 基本用法 在父组件中&#xff0c;你可以这样传递 props&#xff1a; <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 是一个流行的社交媒体平台&#xff0c;它允许用户发送和接收短暂存在的图片和视频。对于开发者来说&#xff0c;访问 Snapchat API 可以为应用程序添加独特的社交功能。本文将介绍如何在 Objective-C 中实现对 Snapchat API 的访问&#xff0c;并提供一个详细的代码示…...

ps证件照蓝底换白底

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

阿里云kafka消息写入topic失败

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

图像放大效果示例【JavaScript】

实现效果&#xff1a; 当鼠标悬停在小图&#xff08;缩略图&#xff09;上时&#xff0c;大图&#xff08;预览图&#xff09;会随之更新为相应的小图&#xff0c;并高亮当前悬浮的小图的父元素。 代码&#xff1a; 1. HTML部分 <!DOCTYPE html> <html lang"z…...

【C#生态园】云端之C#:全面解析6种云服务提供商的SDK

C#开发者必读&#xff1a;深度比较6种云服务SDK 前言 随着云计算技术的迅猛发展&#xff0c;越来越多的企业和开发者选择将应用程序部署到公共云平台上。针对C#开发者而言&#xff0c;各大云服务提供商纷纷推出了适用于C#的SDK&#xff0c;以便开发者能够更轻松地与其云服务进…...

远程升级又双叒叕失败?背后原因竟然是。。。

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

【测试】——Selenium API (万字详解)

&#x1f4d6; 前言&#xff1a;本文详细介绍了如何利用Selenium进行Web自动化测试&#xff0c;包括定位元素&#xff08;如cssSelector和xpath&#xff09;、常用操作函数&#xff08;如点击、输入等&#xff09;、窗口管理、键盘鼠标事件和浏览器导航&#xff0c;以及处理弹窗…...

Redis:原理+项目实战——Redis实战3(Redis缓存最佳实践(问题解析+高级实现))

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;Redis&#xff1a;原理项目实战——Redis实战2&#xff08;Redis实现短信登录&#xff08;原理剖析代码优化&#xff09;&#x…...

刚刚,Stable Diffusion 2024升级,最强Ai绘画整合包、部署教程(解压即用)

2024Ai技术大爆发的元年 目前两款Ai神器大火 一款是大名鼎鼎的Chat GPT 另外一款—Stable Diffusion 堪称全球最强Ai绘画工具 Stable Diffusion Ai绘画2024版本更新啦&#xff01; 从4.8.7更新至**4.9版本&#xff01;**更新优化和大模型增加&#xff0c;无需安装&#xf…...

【AIGC】ChatGPT提示词助力高效文献处理、公文撰写、会议纪要与视频总结

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;高效英文文献阅读提示词使用方法 &#x1f4af;高效公文写作提示词使用方法 &#x1f4af;高效会议纪要提示词使用方法 &#x1f4af;高效视频内容分析提示词使用方法 &a…...

centos7更换国内下载源

&#x1f4d6;centos7更换国内下载源 在CentOS 7上更换为国内源可以通过替换 /etc/yum.repos.d/CentOS-Base.repo文件来实现。以下是一些常用的国内源以及如何更换的示例&#xff1a; 阿里云源&#xff1a; mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Bas…...

【Linux】常用指令【更详细,带实操】

Linux全套讲解系列&#xff0c;参考视频-B站韩顺平&#xff0c;本文的讲解更为详细 目录 一、文件目录指令 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数组&#xff0c;从右往左考虑取不取&#xff0c;如果取则问题变成b[0] ~ b[i-1]间找j - 1个数 如果不取&#xff0c;则问题变成b[0] ~ b[i]间找j个数即dfs(i,j) max(dfs(i-1,j) , dfs(i-1,j-1) a[j] * b[i]) 边界&#xff1a…...

Python | Leetcode Python题解之第413题等差数列划分

题目&#xff1a; 题解&#xff1a; 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&#xff0c;所以可以从 i2 开始枚举for i in range(2, n):i…...

深入理解 ClickHouse 的性能调优与最佳实践

1. 介绍 ClickHouse 是一款由 Yandex 开发的开源列式数据库&#xff0c;专为在线分析处理&#xff08;OLAP&#xff09;场景设计。它以极高的查询性能著称&#xff0c;尤其适用于大规模数据的快速聚合和分析。自发布以来&#xff0c;ClickHouse 在多个行业中得到了广泛应用&am…...

Elasticsearch——介绍、安装与初步使用

目录 1.初识 Elasticsearch1.1.了解 ES1.1.1.Elasticsearch 的作用1.1.2.ELK技术栈1.1.3.Elasticsearch 和 Lucene1.1.4.为什么不是其他搜索技术&#xff1f;1.1.5.总结 1.2.倒排索引1.2.1.正向索引1.2.2.倒排索引1.2.3.正向和倒排 1.3.Elasticsearch 的一些概念1.3.1.文档和字…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

基于IDIG-GAN的小样本电机轴承故障诊断

目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) ​梯度归一化(Gradient Normalization)​​ (2) ​判别器梯度间隙正则化(Discriminator Gradient Gap Regularization)​​ (3) ​自注意力机制(Self-Attention)​​ 3. 完整损失函数 二…...