设计模式——Java × Python 双语言精讲 · 23 个经典模式 + 现代演进(前言篇)

前言:这本书想帮你解决什么

先说结论:你不需要背下 23 个模式的定义,你需要的是在某个深夜改需求时,脑子里突然跳出一句「这活儿适配器能干」。

我见过太多这样的情况:面试时能把单例、工厂、观察者的定义背得滚瓜烂熟,真到了项目里,一个 if-else 写了四百行,一个类改一次需求就要回归测试三天。这不是记性问题,是连接没建立起来 ------模式的知识躺在记忆的仓库里,没有和「正在疼的地方」连上线。

所以这本书的写法是反过来的:先让你疼,再给你药,最后给药起个名字。 每一章都从一个具体的、你大概率遇到过的烂摊子开始,先看清楚它烂在哪,再一步步把它拆开、重组,最后你会发现------你刚刚写出来的那坨东西,教科书上叫「策略模式」。

这本书的四个承诺

|---------|----------------------------------------------------------------|
| 承诺 | 具体做法 |
| 看得懂 | 每个模式都从一个生活场景切入,不用术语解释术语。原理讲透,不搞「定义---结构图---代码」的机械三段式。 |
| 记得住 | 每个模式配一个「一句话本质」+ 一句口诀 + 一个联想画面。人的记忆靠钩子,不靠重复。 |
| 写得出 | 每个模式给出可直接抄改的 Java 与 Python 骨架代码,标注「哪些行是必须的,哪些行是业务」。骨架比定义有用一百倍。 |
| 用得上 | 每章都有「什么时候别用」和「常见坑」,末尾有易混淆模式对照、重构现场、速查卡。 |

三种读法

赶时间的读法: 只读每章的「一句话本质」+「记忆锚点」+「代码骨架」,一小时能过完 23 个模式,先建立地图。

正常读法: 按顺序读。第一部分之前有第 0 章,那是全书的地基,别跳------很多人学不会模式,是因为地基没打。

查手册的读法:直接跳到第 29 章的速查表 ,找到你怀疑的那个模式,再翻回对应章节。

关于代码: 书里的代码刻意写得「不工程化」------没有日志、没有异常包装、没有依赖注入框架。目的是让你一眼看到模式本身的骨架。真实项目里这些骨架会被各种框架「吃掉」,第 27 章会专门讲这件事。

目录

总纲 先修:模式不是抄代码,是给问题命名

第一篇 创建型模式:把 new 藏起来 1 单例 Singleton 2 工厂方法 Factory Method 3 抽象工厂 Abstract Factory 4 建造者 Builder 5 原型 Prototype

第二篇 结构型模式:把对象搭起来 6 适配器 Adapter 7 桥接 Bridge 8 组合 Composite 9 装饰器 Decorator 10 外观 Facade 11 享元 Flyweight 12 代理 Proxy

第三篇 行为型模式:把流程理顺 13 责任链 Chain of Responsibility 14 命令 Command 15 解释器 Interpreter 16 迭代器 Iterator 17 中介者 Mediator 18 备忘录 Memento 19 观察者 Observer 20 状态 State 21 策略 Strategy 22 模板方法 Template Method 23 访问者 Visitor

第四篇 从「认识」到「会用」 24 易混淆模式对对碰 25 反模式:那些长得像模式的东西 26 重构到模式:五个真实现场 27 模式在今天还灵吗 28 23 之外:被遗漏的与更重要的 29 一页速查表与 30 天记忆计划

总纲 先修:模式不是抄代码,是给问题命名

0.1 一个让你睡不着的需求

假设你在做电商。促销规则是这样的:

普通用户满 100 减 10;VIP 打九折;超级 VIP 打八折再送积分;新用户首单立减 20 但不可叠加;双十一全场满 300 减 80;企业采购走阶梯价......

第一版你写了 40 行 if-else。第二版加了两条规则,变成 90 行。第三版运营说「规则要能配置」,你把它改成 260 行,中间嵌套了三层 switch。

然后有一天,运营跑来说:「能不能加个『限时秒杀』,只对部分商品生效,且优先级最高?」

你打开那个 260 行的文件,光标在第一个 if 上闪了十秒钟,然后你关掉了文件,去泡了杯咖啡。

这个瞬间,就是设计模式存在的全部理由。

不是因为你不会写 if-else,而是因为:你没有一个词,来称呼「这段会变的规则」。 没有词,你就没法把它从主流程里切出去;切不出去,它就只能和主流程长在一起;长在一起,改它就要动全身。

0.2 模式的本质:给问题起名字

软件设计模式(Design Pattern)这个概念,最早不是计算机界发明的。建筑师 Christopher Alexander 在 1977 年的《建筑模式语言》里提出:那些在不同场景下反复出现、可以用同一个方案解决的建筑问题,值得被命名、被记录、被传播。

1994 年,四位作者(江湖人称 GoF,Gang of Four)把这套思路搬到了面向对象编程上,总结了 23 个模式,写成《设计模式:可复用面向对象软件的基础》。这本书成了无数程序员的噩梦------不是因为写错了,而是因为它写得太像字典。

但请记住模式的真正价值,它有三层:

|------------|-----------|-----------------------------------|
| 层次 | 价值 | 例子 |
| 第一层:词汇 | 让沟通成本骤降 | 「这里加个装饰器」五个字,替代五分钟的「你包一层再包一层然后转发」 |
| 第二层:经验 | 避免重复踩坑 | 别人已经用血验证过:观察者不处理好内存泄漏,线上必炸 |
| 第三层:思维 | 改变你看代码的方式 | 看到「一堆 if 判断类型」,本能地想「这里缺一个多态」 |

绝大多数人只拿到了第一层,所以觉得「模式没用,就是八股」。真正的收益在第三层:它训练你识别「变化点」的嗅觉。

0.3 一个模式由四个要素组成

每当你判断「这里该用某个模式」,其实你在回答四个问题。本书每一章都按这四问来组织:

|--------|-------------------|-------------|
| 要素 | 要回答的问题 | 答不上来意味着 |
| 名字 | 它叫什么? | 你和同事没法高效沟通 |
| 问题 | 什么场景下、什么约束下,才需要它? | 你在为了用模式而用模式 |
| 方案 | 类怎么摆、关系怎么连? | 你写出来的东西四不像 |
| 后果 | 用了之后我付出了什么代价? | 你在给自己挖坑 |

最后一项最容易被忽略,也最致命。 所有模式都是「用复杂度换灵活性」的交易。单例换来了全局唯一,代价是可测试性变差;装饰器换来了运行时组合,代价是对象数量暴涨、调试栈变深;观察者换来了解耦,代价是「谁通知了我」变得难以追踪。没有代价的方案不叫模式,叫常识。

0.4 六条原则:模式的「宪法」

23 个模式不是 23 条独立技巧,它们都是下面六条原则在不同场景下的具体落地。理解了原则,你甚至可以自己「发明」模式。

|---------------|------------------------------|-----------------|
| 原则 | 一句话人话版 | 它催生了哪些模式 |
| 单一职责 SRP | 一个类只为一件事负责,别让它既算钱又发短信 | 几乎全部(这是所有拆分的前提) |
| 开闭原则 OCP | 要加新功能时,加新代码,别改老代码 | 策略、工厂方法、装饰器、观察者 |
| 里氏替换 LSP | 子类必须能顶替父类,不能偷偷改契约 | 模板方法、组合、代理 |
| 接口隔离 ISP | 别逼人实现他用不上的方法,接口要小而专 | 适配器、外观、桥接 |
| 依赖倒置 DIP | 依赖抽象,别依赖具体;高层别 import 低层的实现类 | 工厂、策略、观察者、依赖注入 |
| 合成复用 CARP | 能「有一个」就别「是一个」,继承是强耦合 | 装饰器、桥接、组合、代理、状态 |

如果只能记一条,记开闭原则 。它的检验方法极其简单粗暴:

下次加需求时,问自己一句:我是「新增了一个文件」,还是「修改了一个旧文件」? 如果每次需求都要动老文件,说明你的代码没有对扩展开放------模式就该上场了。

0.5 该不该预支抽象:两个坐标

上一节说「只能记一条就记开闭原则」,第 25 章又会说「说不出第二个实现的名字,就别抽接口」。这两句话在字面上是打架 的,先把矛盾摆到台面上:

开闭原则要求你提前: 加需求时只新增文件、不改旧文件------这需要你在需求到来之前,就把扩展点留好。

克制原则要求你延后: 等变化真的发生了再抽象------可等到那时候,旧文件早就该改了。

解决它的不是再补一条原则,而是一个二维判据。任何一个「可能的变化点」,都落在这张图的某个象限里:

|---------------------------|--------------------------|----------------------------------------------------|
| 变化成本 \ 变化概率 | 概率高 | 概率低 |
| 成本高 (改一次要动多个文件、要全量回归) | 值得预支抽象 这是唯一该提前上模式的象限 | 先隔离,别抽象 把逻辑收进一个独立类/函数,但不抽接口。等第二个实现真出现,替换成本只有一处 |
| 成本低 (改一个常量、一行配置) | 不要抽象 抽了只会多一层跳转,收益为零 | 什么都不做 这是绝大多数「想象中的变化」的真实位置 |

判据只有一句:只有「变化概率高」且「变化成本高」同时成立,才值得预支抽象。 其余三种情况,提前上模式都是净亏损。

落到操作层面,用三个问题筛一遍,全部答「是」才动手:

  1. 你能否说出下一个变化点具体长什么样? 不是「以后可能要支持别的」,而是「下个季度要接银联,它的签名算法和退款接口跟现在不一样」。说不出具体形态,说明概率低。
  2. 变化发生时,改动会不会跨三个以上文件? 不会,说明成本低,直接改更划算。
  3. 这个扩展点本身,会不会变成第二个需要维护的东西? 会------比如你为「可能的多币种」抽了一层汇率抽象,结果三年只有人民币,这层抽象每次改动都要跟着改。那它的成本已经抵消了收益。

所以第 25 章那句「说不出第二个实现的名字就别抽」,并不与开闭原则冲突。 它只是把「变化概率」这一维,压缩成了一个更好用的提问方式------因为人对自己项目的未来判断极不可靠,而「现在能不能叫出第二个实现的名字」是一个可以立刻验证 的事实。

开闭原则的真正含义也不是「所有地方都留扩展点」,而是「在你判断会变的地方 留扩展点」。判断,就是上面那两个坐标。 把「开闭」当成无条件的教条,才是过度设计的源头。

记住这个顺序:先判概率,再判成本,最后才选模式。 反过来(先挑一个模式,再找地方用它)就是全书反复警告的那种失败。

0.6 全景地图:23 个模式速览

GoF 把 23 个模式分成三类,分类依据是它们各自在解决什么维度的问题 :

  1. 创建型(5 个) :管「对象怎么来」。解决 new 太硬、太散、太复杂的问题。
  2. 结构型(7 个) :管「对象怎么摆」。解决类与类之间的组合关系问题。
  3. 行为型(11 个) :管「对象怎么动」。解决职责分配与算法流转的问题。

|---------|--------|--------------------|
| 类别 | 模式 | 一句话本质 |
| 创建型 | 单例 | 全局只有一个,谁拿都是它 |
| 创建型 | 工厂方法 | 造什么由子类决定,调用方只管用 |
| 创建型 | 抽象工厂 | 一整套配套产品,一起造、不许混搭 |
| 创建型 | 建造者 | 复杂对象分步组装,最后一步交货 |
| 创建型 | 原型 | 复制一个现成的,比从零造便宜 |
| 结构型 | 适配器 | 接口对不上,中间塞个翻译 |
| 结构型 | 桥接 | 两个维度各自变化,别用继承把它们焊死 |
| 结构型 | 组合 | 整体和部分一视同仁,树形递归 |
| 结构型 | 装饰器 | 不改变原类,一层层往上加功能 |
| 结构型 | 外观 | 一堆子系统,给个一键入口 |
| 结构型 | 享元 | 相同的部分共享,不同的部分外传 |
| 结构型 | 代理 | 用替身挡在前面,控制访问 |
| 行为型 | 责任链 | 一环传一环,谁处理得了谁处理 |
| 行为型 | 命令 | 把「请求」打包成对象,可排队可撤销 |
| 行为型 | 解释器 | 为一个小语言定义文法并求值 |
| 行为型 | 迭代器 | 只管一个个取,不管底下怎么存 |
| 行为型 | 中介者 | 网状沟通改成星形,都找中间人 |
| 行为型 | 备忘录 | 拍个快照,随时能回滚 |
| 行为型 | 观察者 | 一变多通知,发布订阅 |
| 行为型 | 状态 | 状态变了,行为跟着换,别写 if |
| 行为型 | 策略 | 算法可替换,运行时挑一个 |
| 行为型 | 模板方法 | 流程骨架定死,个别步骤留给子类 |
| 行为型 | 访问者 | 数据结构不变,操作可以随便加 |

0.7 全书口诀:先记住这一首

创建五兄弟 :单例唯一、工厂分身、抽象成套、建造分步、原型克隆。

结构七件套 :适配翻译、桥接双维、组合成树、装饰加料、外观收口、享元共享、代理挡门。

行为十一式 :链上传递、命令封装、解释文法、迭代遍历、中介居中、备忘回滚、观察广播、状态换脸、策略换算法、模板定框、访问加分派。

顺口溜的作用不是让你理解,是让你在面试前三十秒、在会议室白板前,能迅速把 23 个格子填满。理解靠后面的 23 章。

相关推荐
骇客野人44 分钟前
Linux 全套实操方案(含目录详解、文件传输、日志、文件规范、环境变量、Java 应用部署运维)
java·linux·运维
_菜鸟果果1 小时前
高德地图 JS API 2.0 在国产 Linux/ARM 电脑上渲染失败 的排查与解决
linux·开发语言·javascript
.Bryce.1 小时前
MCP+A2A+ANP,Agent通信协议的完整知识地图
java·人工智能·后端·agent·通信协议
SL_staff1 小时前
规则引擎选型陷阱:为什么QPS不是JVS-Rules对比Drools的核心指标
java·开源·全栈
LMT流氓兔1 小时前
第五单元——第4课:给工具参数加校验
java·前端·算法·ai·langchain
昂好的day1 小时前
VisionMaster(VM) 标定工具详解(上):为什么必须标定 + 九点标定手眼标定
开发语言·数码相机·c#
eleven_4u1 小时前
RocketMQ相关问题
java·运维·中间件
严谨的麻辣烫1 小时前
静态代理 IP 的出口一致性如何验证?Python 请求检测与异常排查
开发语言·python·http·网络安全
迷途小羔羊2 小时前
项目概述
java·javascript·后端