设计模式(Python)——聊聊那些被高估和低估的套路
先说明,我写这篇不是在给你背 GoF 那本经典书里的类图,而是想聊聊“设计模式”这四个字放在 Python 语境下,到底该怎么理解、怎么用、怎么应付大作业和面试。
最近和几个朋友在搞多 agent 应用,聊到一个很有意思的观点:现代多 agent 设计里的“主从模式”,其实本质上就是把 subagent 当作一种另类的 tool 来调用。这个思路一出来,我突然觉得设计模式这个概念从来没有过时,它只是换了一副面孔继续存在于我们的架构里。无论是传统的 23 种 GoF 模式,还是现在 AI agent 的工具调用,背后都是同一套工程思维:把可复用的经验沉淀成结构,用稳定的结构对抗复杂度。
这篇文章我会从几个角度展开:为什么在 Python 里要重新理解设计模式、哪些模式最值得掌握、传统模式和 agent 场景之间的映射关系、以及大作业和面试怎么脱颖而出。全程用我自己踩过的坑和实测过的代码说话,不绕弯子。
1. 为什么我劝你在 Python 里重新理解设计模式
1.1 设计模式的本质是“场景经验的压缩包”
很多人一开始学设计模式就背类图,这是最大的误区。设计模式不是什么高深的数学定理,它更像是一个“场景经验的压缩包”。打个比方,你在生活中遇到了“出门要带钥匙、手机、钱包”这件事,总结出一个习惯叫“出门三件套”。下次再出门不用重新想,直接按这个套路来。设计模式就是工程界的“三件套”:当某个问题场景反复出现,前人把解决方案、推导过程、注意事项打包在一起,起个名字,比如“观察者模式”“策略模式”,大家一听到名字就懂你在说什么。
所以在 Python 里学设计模式,第一步不是背 UML,而是建立“场景—方案”的联想。看到“运行时需要切换算法”,想到策略模式;看到“一个对象状态变化要通知一堆依赖者”,想到观察者模式;看到“创建对象的过程需要别处统一管理”,想到工厂模式。这样学,面试时你能聊出花来,写代码时也知道该往哪个方向走。
我见过太多人把设计模式当成“代码规范”来批判,说“这么简单的功能还要套一层,不是浪费吗”。这种批评有一定道理,但更多是对模式的误解。模式不是为了炫技,而是为了应对“变化”和“复杂”。如果一段代码从头到尾只写一次、永远不会变、没有任何协作者,那确实不需要模式。但现实项目里,需求会变、团队会换、接口会迭代,模式的价值就体现在这里。
1.2 Python 让很多模式“隐形”了
Python 这门语言有一个特点:很多在 Java/C++ 里必须用模式解决的问题,在 Python 里被语言特性直接解决了。这不是说模式没用了,而是说有些模式的“实现成本”被降到极低,甚至看起来像消失了一样。
举个最典型的例子:策略模式。在 Java 里你通常要定义一个策略接口、写多个实现类、再用一个上下文类来持有策略对象。而在 Python 里,函数是一等公民,直接在调用处传一个函数就行。你看不出“策略模式”的影子,但它确实在起作用。
再比如迭代器模式。Java 里要写 Iterator 接口、hasNext()、next() 方法,Python 里只需实现 __iter__ 和 __next__,更多时候直接用 for 循环加生成器就完事了。命令模式在 Python 里可以用函数对象或 functools.partial 轻量替代;单例模式直接用模块天然实现;外观模式往往就是一个 __init__.py 里导出的公共函数。
我把经典模式在 Python 里的“形态变化”整理成一个表格,你看看就明白了:
| GoF 分类 | 模式名称 | 传统实现形态 | Python 常见形态 |
|---|---|---|---|
| 创建型 | 单例模式 | 私有构造 + 静态实例 | 模块级唯一实例 / functools.lru_cache |
| 创建型 | 工厂方法 | 抽象工厂类 + 子类覆写 | 类方法 from_xxx / 字典注册表 |
| 结构型 | 适配器模式 | 实现目标接口并包装源类 | 封装一个函数包装另一个函数,非常轻量 |
| 结构型 | 外观模式 | 门面类组合子系统 | __init__.py 统一导出公共 API |
| 行为型 | 策略模式 | 策略接口 + 策略类 | 普通函数作为参数传入 |
| 行为型 | 命令模式 | 命令接口 + 命令类 | 函数对象 / partial / 闭包 |
| 行为型 | 观察者模式 | 观察者接口 + 主题维护列表 | 事件回调 / asyncio 信号 / 自定义装饰器 |
| 行为型 | 迭代器模式 | Iterator 接口 | 生成器 / __iter__ / for 协议 |
所以我的结论是:在 Python 里学设计模式,重点不是逐字复刻类图,而是理解每个模式解决的核心冲突,然后用 Python 的方式给出最简洁的落地。你要是一板一眼地把 Java 的工厂类搬到 Python,代码会比原来更啰嗦,这反而违背了模式最初的目的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Python 里最值得先掌握的 6 种模式
2.1 策略模式:从 if/elif 到选择器
策略模式是我最推荐 Python 初学者先掌握的模式,因为它的日常使用频率太高了。最常见的坏味道,就是业务代码里堆了一长串 if/elif 来根据条件执行不同算法。
举个例子,假设你写一个订单折扣系统,打折规则有好几种:新人折扣、满减、会员折扣、节日折扣。新手写法是一大坨逻辑堆在主函数里,时间一长谁也改不动。策略模式的思路很简单:把每种折扣规则封装成独立策略,然后在运行时选择。
Python 里最常见的轻量实现,直接用字典做注册表:
python复制from typing import Callable, Dict
def discount_newbie(price: float) -> float:
return price * 0.9
def discount_full_reduction(price: float) -> float:
return price - 50 if price >= 500 else price
def discount_vip(price: float) -> float:
return price * 0.75
# 注册表:把策略名映射到实现函数
discount_strategies: Dict[str, Callable[[float], float]] = {
"newbie": discount_newbie,
"full_reduction": discount_full_reduction,
"vip": discount_vip,
}
def apply_discount(price: float, strategy_name: str) -> float:
strategy = discount_strategies.get(strategy_name)
if strategy is None:
raise ValueError(f"unknown strategy: {strategy_name}")
return strategy(price)
以后加新策略,只需要新增一个函数、在字典里注册一行,主流程完全不用动。这就是策略模式在 Python 里最自然的形态。我实际工作中已经很少为策略模式单独建类了,除非策略本身足够复杂、需要维护内部状态,那种情况下才考虑类封装。
这套“注册表 + 函数”的组合在 agent 场景里也经常用到。比如给 LLM 定义不同的处理工具,配置一个 name 到 handle
