设计模式
设计模式
设计模式(Design Patterns)是软件设计中反复出现的问题的成熟解决方案,是前辈工程师经验的总结。它们不是代码库,而是"解决特定问题的思路模板"。
本教程内容
本教程系统介绍 23 个经典 GoF 设计模式(创建型、结构型、行为型)以及 10 个 J2EE 模式,每个模式包含:意图、结构、Python 代码示例、适用场景与优缺点。
为什么学习设计模式
- 沟通语言:说"单例"比解释十分钟更高效
- 复用经验:站在前人的肩膀上解决问题
- 规范设计:让代码可扩展、可维护
- 面试必备:高级岗位的常见考点
模式分类
# 创建型(5): 对象的创建
工厂 / 抽象工厂 / 单例 / 建造者 / 原型
# 结构型(7): 对象的组合
适配器 / 桥接 / 组合 / 装饰器 / 外观
享元 / 代理
# 行为型(11): 对象间的交互
责任链 / 命令 / 解释器 / 迭代器 / 中介者
备忘录 / 观察者 / 状态 / 策略 / 模板 / 访问者
# J2EE 模式(10): 表现层/服务层
MVC / 业务代表 / 组合实体 / DAO / 前端控制器
拦截过滤器 / 服务定位器 / 传输对象 等
学习建议
# 1. 先理解"解决什么问题",再记结构
# 2. 每个模式动手写一遍示例
# 3. 在真实项目中识别模式的使用场景
# 4. 不要滥用: 简单场景用简单方案
# 5. 模式是工具,不是目标
# 黄金法则:
# 简单问题不要为了"用模式"而用模式
# 当代码开始"重复/难以修改"时,
# 才考虑用模式重构
设计模式是工程师的"内功心法"。掌握这些套路,代码设计与沟通都会上一个台阶。
设计模式简介
设计模式简介
设计模式源于建筑学,由"四人组"(Gang of Four, GoF)于 1994 年在《Design Patterns》一书中系统化,奠定了面向对象设计模式的经典体系。
核心原则
# SOLID 原则(设计模式的基础):
# S - 单一职责: 一个类只做一件事
# O - 开闭原则: 对扩展开放,对修改关闭
# L - 里氏替换: 子类可替换父类
# I - 接口隔离: 最小接口
# D - 依赖倒置: 依赖抽象,不依赖具体
# 设计模式多数在实践这些原则:
# - 工厂/抽象工厂 → 开闭原则
# - 观察者 → 依赖倒置
# - 装饰器 → 开闭原则
# - 适配器 → 接口隔离
模式要素
# 每个模式包含四个要素:
# 1. 模式名称: 简洁的命名(沟通语言)
# 2. 问题: 何时使用(适用场景)
# 3. 解决方案: 结构与参与者
# 4. 效果: 优缺点与权衡
# 例: 单例模式
# 名称: Singleton
# 问题: 全局唯一实例(配置/连接池)
# 方案: 私有构造 + 静态获取
# 效果: 省资源,但难测试/难扩展
如何选择模式
# 按需求选择:
# 1. 创建对象复杂 → 工厂/建造者
# 2. 需要唯一实例 → 单例
# 3. 接口不匹配 → 适配器
# 4. 动态加功能 → 装饰器
# 5. 一对多通知 → 观察者
# 6. 算法可替换 → 策略
# 7. 行为随状态变 → 状态
# 8. 对象结构复杂 → 组合
# 9. 控制访问 → 代理
# 10. 解耦请求处理 → 责任链
# 误用信号:
# - 代码比不用模式更复杂
# - 为了"炫技"而套模式
# - 模式导致过度设计
模式的辩证看法
# 优点:
# - 经验沉淀,减少试错
# - 代码更易读(模式即文档)
# - 便于团队协作
# 缺点/风险:
# - 增加抽象与复杂度
# - 可能过度设计
# - 语言特性可简化部分模式
# (Python 中单例可用模块、装饰器更简)
# 现代视角:
# - 函数式编程提供了新思路
# - 设计模式是"工具包"而非"教条"
# - 简单 > 模式: 优先可读性与简洁性
Python 中的设计模式
# Python 动态特性简化了部分模式:
# 单例 → 模块级对象
# 迭代器 → 生成器
# 命令 → 函数/闭包
# 策略 → 函数参数
# 但仍推荐学习经典模式:
# - 理解主流代码库(JAVA/C#)
# - 大型项目分层与扩展
# - 面试与设计能力
# 学习路径:
# 1. 掌握 SOLID 原则
# 2. 熟悉 23 个 GoF 模式(本教程)
# 3. 结合真实场景实践
# 4. 反思: 何时该用、何时不该用
设计模式是"经验结晶"。带着"解决什么问题"的视角学习,在实践中拿捏分寸,才是正确的打开方式。
工厂模式
工厂模式(Factory Pattern)
工厂模式:用一个"工厂"创建对象,客户端不直接 new,而是通过工厂获取。解耦"创建"与"使用"。
意图
# 问题: 客户端直接创建对象,耦合具体类
# 变化: 新增产品类型 → 修改所有创建处
# 解决: 将创建逻辑集中到工厂
# 客户端只依赖"产品接口"与"工厂"
# 优点:
# - 解耦创建与使用
# - 新增产品只需扩展工厂
# - 创建逻辑集中管理
# 缺点:
# - 每加一个产品要改工厂(简单工厂)
# - 增加类数量
结构
# 参与者:
# Product: 产品接口
# ConcreteProduct: 具体产品
# Factory: 工厂(负责创建)
# 关系:
# Client → Factory → Product
Python 示例
from abc import ABC, abstractmethod
# 产品接口
class Transport(ABC):
@abstractmethod
def deliver(self) -> str: ...
# 具体产品
class Truck(Transport):
def deliver(self) -> str:
return "用卡车运输"
class Ship(Transport):
def deliver(self) -> str:
return "用轮船运输"
# 简单工厂(静态方法)
class TransportFactory:
@staticmethod
def create(t_type: str) -> Transport:
if t_type == "truck":
return Truck()
elif t_type == "ship":
return Ship()
raise ValueError(f"未知类型: {t_type}")
# 客户端
def client(factory, t_type: str):
transport = factory.create(t_type)
return transport.deliver()
print(client(TransportFactory, "truck"))
# 用卡车运输
# 扩展: 新增"火车"只需加产品类 + 工厂分支
class Train(Transport):
def deliver(self) -> str:
return "用火车运输"
工厂方法(Factory Method)
# 变体: 工厂方法(每个产品一个工厂)
class Logistics(ABC):
@abstractmethod
def create_transport(self) -> Transport: ...
def plan_delivery(self) -> str:
# 模板方法: 使用产品
t = self.create_transport()
return f"配送方案: {t.deliver()}"
class RoadLogistics(Logistics):
def create_transport(self) -> Transport:
return Truck()
class SeaLogistics(Logistics):
def create_transport(self) -> Transport:
return Ship()
# 客户端只与 Logistics 打交道
road = RoadLogistics()
print(road.plan_delivery()) # 用卡车运输
# 新增产品: 加产品类 + 加对应工厂,不改旧代码
# (符合开闭原则)
适用场景
# 1. 创建对象逻辑复杂(多参数/条件分支)
# 2. 运行时才确定具体类型(配置/参数驱动)
# 3. 需要统一管理对象的创建(日志/计数)
# 实际案例:
# - 数据库连接工厂(MySQL/PostgreSQL)
# - 支付工厂(支付宝/微信/银联)
# - 解析器工厂(JSON/XML/YAML)
# - 消息队列生产/消费者工厂
# Python 简化:
# 简单场景直接用 dict 映射
TRANSPORTS = {
"truck": Truck,
"ship": Ship,
}
def create(t_type: str):
cls = TRANSPORTS.get(t_type)
if not cls:
raise ValueError(f"未知: {t_type}")
return cls()
优缺点总结
# 优点:
# ✅ 解耦创建与使用
# ✅ 创建逻辑集中
# ✅ 符合开闭原则(工厂方法)
# 缺点:
# ❌ 类数量增加
# ❌ 简单场景过度设计
# ❌ 复杂产品层次时工厂复杂
# 决策:
# 简单创建 → 直接用构造器/dict 工厂
# 复杂/多变产品族 → 工厂方法/抽象工厂
工厂模式是"创建对象"的解耦利器。掌握简单工厂与工厂方法,新增产品类型不再牵一发动全身。
抽象工厂模式
抽象工厂模式(Abstract Factory)
抽象工厂:创建"一族相关对象"的工厂。与工厂方法的区别:工厂方法创建一种产品,抽象工厂创建一族(多个类型)产品。
意图
# 问题: 需要创建一组配套的对象
# 例: UI 主题(按钮+输入框+对话框必须风格一致)
# 解决: 抽象工厂接口 + 具体工厂(每族一个)
# 客户端通过工厂获取整族产品,保证配套
# 优点:
# - 保证产品族一致性
# - 客户端与具体类解耦
# - 换族只需换工厂
# 缺点:
# - 新增产品类型困难(改所有工厂)
# - 增加类数量
结构
# 参与者:
# AbstractFactory: 抽象工厂(创建族中每种产品)
# ConcreteFactory: 具体工厂(一种风格/平台)
# AbstractProduct: 抽象产品(每类产品的接口)
# ConcreteProduct: 具体产品
# 例:
# UIFactory(抽象)
# ├── LightFactory(浅色族)
# └── DarkFactory(深色族)
# 每族都生产 Button / Input / Dialog
Python 示例
from abc import ABC, abstractmethod
# 抽象产品
class Button(ABC):
@abstractmethod
def render(self) -> str: ...
class Dialog(ABC):
@abstractmethod
def show(self) -> str: ...
# 浅色族产品
class LightButton(Button):
def render(self) -> str:
return "浅色按钮"
class LightDialog(Dialog):
def show(self) -> str:
return "浅色对话框"
# 深色族产品
class DarkButton(Button):
def render(self) -> str:
return "深色按钮"
class DarkDialog(Dialog):
def show(self) -> str:
return "深色对话框"
# 抽象工厂
class UIFactory(ABC):
@abstractmethod
def create_button(self) -> Button: ...
@abstractmethod
def create_dialog(self) -> Dialog: ...
# 具体工厂(浅色族)
class LightFactory(UIFactory):
def create_button(self) -> Button:
return LightButton()
def create_dialog(self) -> Dialog:
return LightDialog()
# 深色族
class DarkFactory(UIFactory):
def create_button(self) -> Button:
return DarkButton()
def create_dialog(self) -> Dialog:
return DarkDialog()
# 客户端: 依赖抽象工厂
def render_ui(factory: UIFactory) -> str:
btn = factory.create_button()
dlg = factory.create_dialog()
return f"{btn.render()} + {dlg.show()}"
print(render_ui(LightFactory()))
# 浅色按钮 + 浅色对话框
print(render_ui(DarkFactory()))
# 深色按钮 + 深色对话框
# 切换主题: 换工厂即可,客户端零改动
典型场景
# 1. UI 主题/风格体系
# 2. 跨平台组件(Windows/Mac/Linux 控件)
# 3. 数据库操作族(连接+游标+事务)
# 4. 支付体系(支付宝族: 支付+退款+查询)
# 5. 报告生成(PDF 族/Excel 族: 头+体+尾)
# Python 生态示例:
# - Django settings 多环境配置
# - 多后端缓存(cache backends)
# - 测试替身工厂(mock factory)
# 实际案例: 主题切换
theme = DarkFactory() if dark_mode else LightFactory()
ui = render_ui(theme)
工厂方法 vs 抽象工厂
# 工厂方法:
# 创建"一种"产品
# 通过继承扩展(子类重写工厂方法)
# 抽象工厂:
# 创建"一族"相关产品
# 通过组合扩展(注入具体工厂)
# 选择:
# 单一产品变化 → 工厂方法
# 产品族配套 → 抽象工厂
# 示例对比:
# 创建"运输工具"(一种) → 工厂方法
# 创建"UI 组件族"(多种配套) → 抽象工厂
优缺点总结
# 优点:
# ✅ 产品族一致性
# ✅ 解耦客户端
# ✅ 符合开闭原则(新增族易)
# 缺点:
# ❌ 新增产品类型难(违反开闭)
# ❌ 类爆炸(每族每产品一个类)
# 简化(Python):
# 用 dict 注册工厂
FACTORIES = {
"light": LightFactory,
"dark": DarkFactory,
}
factory = FACTORIES[theme_name]()
抽象工厂解决"产品族配套"问题。掌握族的组织方式,主题、跨平台、多后端等场景应对自如。
单例模式
单例模式(Singleton Pattern)
单例:确保一个类只有一个实例,并提供全局访问点。用于配置、日志、连接池等全局唯一对象。
意图
# 问题: 某些对象应该全局唯一
# - 配置中心(读取一份配置)
# - 日志器(统一写入)
# - 数据库连接池(复用连接)
# 解决: 控制构造,保证只创建一个实例
# 优点:
# - 全局唯一实例
# - 节省资源(懒加载)
# - 全局访问点
# 缺点:
# - 隐藏依赖(全局状态难测试)
# - 违反单一职责(创建+业务)
# - 并发初始化需注意
Python 实现方式
# 方式1: 模块级单例(最简单,推荐)
# config.py
class Config:
def __init__(self):
self.debug = False
self.db_url = "sqlite:///app.db"
config = Config() # 模块加载时创建一次
# 使用
from config import config
print(config.debug)
# 方式2: __new__ 控制
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
# 注意: __init__ 每次调用都会执行
# 需自行控制初始化
pass
a = Singleton()
b = Singleton()
print(a is b) # True(同一实例)
线程安全单例
import threading
class ThreadSafeSingleton:
_instance = None
_lock = threading.Lock()
def __new__(cls):
# 双检锁: 先查后锁,减少锁开销
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
# 验证多线程下唯一
def worker():
s = ThreadSafeSingleton()
assert s is ThreadSafeSingleton._instance
threads = [threading.Thread(target=worker)
for _ in range(10)]
for t in threads: t.start()
for t in threads: t.join()
print("所有线程拿到同一实例")
元类单例(进阶)
# 用元类统一实现
class SingletonMeta(type):
_instances = {}
_lock = threading.Lock()
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
with cls._lock:
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Logger(metaclass=SingletonMeta):
def __init__(self):
self.logs = []
def log(self, msg):
self.logs.append(msg)
print(f"[LOG] {msg}")
log1 = Logger()
log2 = Logger()
print(log1 is log2) # True
log1.log("hello")
print(len(log2.logs)) # 1(共享状态)
装饰器单例
def singleton(cls):
instances = {}
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Database:
def __init__(self):
self.connected = False
self._init_connection()
def _init_connection(self):
# 模拟连接
print("创建数据库连接")
self.connected = True
db1 = Database() # 创建数据库连接
db2 = Database() # (不再创建)
print(db1 is db2) # True
适用场景与反模式
# 适用:
# ✅ 配置中心
# ✅ 日志器
# ✅ 连接池/线程池
# ✅ 缓存管理器
# 慎用/反模式:
# ❌ 隐藏依赖的全局状态(难测试)
# ❌ 业务对象强行单例
# ❌ 仅为了"方便访问"而单例
# 现代实践:
# - 依赖注入(DI)替代单例(可测试)
# - Python 模块天然单例(优先)
# - functools.lru_cache 做"值单例"
# 依赖注入示例
class App:
def __init__(self, config): # 注入
self.config = config
# 测试时传入 mock 配置,无需单例
优缺点总结
# 优点:
# ✅ 唯一实例,省资源
# ✅ 全局访问方便
# ✅ 懒加载
# 缺点:
# ❌ 全局状态,测试困难
# ❌ 隐藏依赖关系
# ❌ 多线程需注意
# 决策:
# 简单场景 → 模块级对象
# 需要延迟/控制 → __new__ 或元类
# 团队项目 → 考虑依赖注入替代
单例是"唯一实例"的保障。掌握 Python 的多种实现(模块/__new__/元类/装饰器),按场景选择,避免滥用。
建造者模式
建造者模式(Builder Pattern)
建造者:将复杂对象的构建过程与表示分离,让同一构建过程可以创建不同表示。
意图
# 问题: 对象构造参数多、步骤复杂
# - 直接构造器: 参数顺序/可选参数难管理
# - 大量 setter: 忘记设置导致不完整
# 解决: 分步构建,一步一个方法
# 最后调用 build() 产出完整对象
# 优点:
# - 构建过程清晰(方法名即步骤)
# - 校验在 build() 统一做
# - 可复用构建流程
# - 参数多时比构造器更可读
# 缺点:
# - 类数量增加
# - 简单对象没必要
结构
# 参与者:
# Director: 指挥者(可选,编排构建步骤)
# Builder: 构建器接口(分步方法)
# ConcreteBuilder: 具体构建器
# Product: 最终产品
# 简化版(常用):
# Builder: 链式方法 + build()
# 无需 Director(客户端直接调用)
Python 示例: 链式建造者
class Pizza:
def __init__(self):
self.size = "中"
self.cheese = False
self.meat = []
self.veggies = []
self.sauce = "番茄"
def __str__(self):
return (f"Pizza({self.size}, cheese={self.cheese}, "
f"meat={self.meat}, veggies={self.veggies}, "
f"sauce={self.sauce})")
class PizzaBuilder:
def __init__(self):
self.pizza = Pizza()
# 链式方法(返回 self)
def set_size(self, size: str) -> "PizzaBuilder":
self.pizza.size = size
return self
def add_cheese(self) -> "PizzaBuilder":
self.pizza.cheese = True
return self
def add_meat(self, meat: str) -> "PizzaBuilder":
self.pizza.meat.append(meat)
return self
def add_veggie(self, veggie: str) -> "PizzaBuilder":
self.pizza.veggies.append(veggie)
return self
def set_sauce(self, sauce: str) -> "PizzaBuilder":
self.pizza.sauce = sauce
return self
def build(self) -> Pizza:
# 构建前校验
if not self.pizza.meat and not self.pizza.veggies:
raise ValueError("披萨至少需要一种配料")
return self.pizza
# 使用: 链式调用,可读性强
pizza = (PizzaBuilder()
.set_size("大")
.add_cheese()
.add_meat("培根")
.add_veggie("蘑菇")
.build())
print(pizza)
# Pizza(大, cheese=True, meat=['培根'], ...)
带 Director 的版本
# Director: 预定义常用构建流程
class PizzaDirector:
def __init__(self, builder: PizzaBuilder):
self.builder = builder
def build_veggie_pizza(self) -> Pizza:
return (self.builder
.set_size("中")
.add_veggie("青椒")
.add_veggie("蘑菇")
.build())
def build_meat_lover(self) -> Pizza:
return (self.builder
.set_size("大")
.add_cheese()
.add_meat("培根")
.add_meat("香肠")
.build())
director = PizzaDirector(PizzaBuilder())
print(director.build_veggie_pizza())
print(director.build_meat_lover())
与直接构造对比
# 直接构造(参数多时混乱)
# Pizza(size="大", cheese=True, meat=["培根"],
# veggies=[], sauce="番茄", extra=True, ...)
# 建造者(步骤清晰)
# PizzaBuilder().set_size("大").add_cheese()
# .add_meat("培根").build()
# 选择:
# 参数 ≤3 且简单 → 直接构造
# 参数多/步骤多 → 建造者
# 或 Python dataclass + 默认值
# dataclass 简化(现代推荐)
from dataclasses import dataclass, field
@dataclass
class PizzaDC:
size: str = "中"
cheese: bool = False
meat: list = field(default_factory=list)
veggies: list = field(default_factory=list)
sauce: str = "番茄"
pizza = PizzaDC(size="大", cheese=True,
meat=["培根"])
# 参数化构造已够用,无需 Builder
适用场景
# 1. 对象参数多且可选(配置对象/请求对象)
# 2. 构建步骤固定但表示不同(同一流程多产物)
# 3. 构建过程需要校验与默认值
# 实际案例:
# - HTTP 请求构建(URL/头/体/超时)
# - 数据库查询构建(select/where/order)
# - 邮件构建(收件人/主题/附件)
# - 测试数据工厂
# 示例: 查询构建器
class QueryBuilder:
def __init__(self, table):
self._table = table
self._where = []
self._order = None
def where(self, cond: str) -> "QueryBuilder":
self._where.append(cond)
return self
def order_by(self, field: str) -> "QueryBuilder":
self._order = field
return self
def build(self) -> str:
sql = f"SELECT * FROM {self._table}"
if self._where:
sql += " WHERE " + " AND ".join(self._where)
if self._order:
sql += f" ORDER BY {self._order}"
return sql
sql = (QueryBuilder("users")
.where("age > 18")
.where("active = 1")
.order_by("created_at")
.build())
print(sql)
# SELECT * FROM users WHERE age > 18 AND active = 1
# ORDER BY created_at
优缺点总结
# 优点:
# ✅ 构建过程清晰
# ✅ 分步校验
# ✅ 复用构建流程(Director)
# ✅ 链式 API 体验好
# 缺点:
# ❌ 类数量增加
# ❌ 简单对象过度设计
# ❌ 链式 API 调试稍繁琐
# 决策:
# 简单参数 → dataclass/直接构造
# 复杂多步构建 → Builder
# Python 中链式 API 很自然(推荐)
建造者把"复杂构建"变得清晰有序。掌握链式调用与校验,配置类/查询类对象的构造优雅可读。
原型模式
原型模式(Prototype Pattern)
原型:通过"克隆已有对象"来创建新对象,而非重新构建。适合创建成本高或结构复杂的对象。
意图
# 问题: 创建对象成本高(数据库/复杂初始化)
# 或需要与现有对象"基本一致,略有不同"
# 解决: 以对象为原型,克隆出副本再修改
# 优点:
# - 创建成本低(复制而非重建)
# - 避免与创建逻辑耦合
# - 运行时动态创建相似对象
# 缺点:
# - 浅拷贝陷阱(嵌套对象共享)
# - 克隆接口实现需注意
# Python 中天然支持:
# copy.copy(浅拷贝) / copy.deepcopy(深拷贝)
# 或自定义 clone() 方法
Python 示例: 深浅拷贝
import copy
class Document:
def __init__(self, title, content, tags=None):
self.title = title
self.content = content
self.tags = tags or []
def __str__(self):
return (f"Document({self.title}, "
f"tags={self.tags})")
# 原型对象
template = Document("模板", "默认内容",
tags=["通用"])
# 浅拷贝: 新对象,但嵌套对象共享
doc1 = copy.copy(template)
doc1.title = "文档A"
doc1.tags.append("A") # 影响 template!
print(template.tags) # ['通用', 'A'] ← 被改了!
# 深拷贝: 完全独立
doc2 = copy.deepcopy(template)
doc2.title = "文档B"
doc2.tags.append("B")
print(template.tags) # ['通用'](不受影响)
print(doc2.tags) # ['通用', 'B']
自定义 clone 方法
from abc import ABC, abstractmethod
import copy
class Prototype(ABC):
@abstractmethod
def clone(self): ...
class Report(Prototype):
def __init__(self, title, data, config=None):
self.title = title
self.data = data
self.config = config or {}
def clone(self, **overrides):
"""克隆并支持覆盖属性"""
new = copy.deepcopy(self)
for key, value in overrides.items():
setattr(new, key, value)
return new
# 使用
base = Report("销售报告", {"total": 1000},
{"format": "pdf"})
monthly = base.clone(title="三月销售报告",
data={"total": 1500})
print(monthly.title) # 三月销售报告
print(monthly.data) # {'total': 1500}
# base 不受影响
print(base.title) # 销售报告
场景示例: 游戏对象
import copy
class Enemy:
def __init__(self, name, health, damage,
skills=None):
self.name = name
self.health = health
self.damage = damage
self.skills = skills or []
def clone(self, **mods):
new = copy.deepcopy(self)
for k, v in mods.items():
setattr(new, k, v)
return new
# 原型: 精英怪模板
elite_template = Enemy("精英守卫", 500, 30,
skills=["重击", "格挡"])
# 快速生成关卡怪物(每个略有不同)
enemy1 = elite_template.clone(health=600)
enemy2 = elite_template.clone(name="精英射手",
damage=40,
skills=["射击"])
enemy3 = elite_template.clone(health=450, damage=25)
# 大量相似对象: 克隆远比重新构建高效
spawns = [elite_template.clone()
for _ in range(100)]
与工厂模式的对比
# 工厂模式: 从零创建(新对象)
# 原型模式: 克隆创建(基于已有对象)
# 选择:
# - 对象结构简单、创建便宜 → 工厂
# - 对象结构复杂、创建贵 → 原型
# - 需要"相似副本" → 原型
# 实际案例:
# - 文档/报表模板复制
# - 配置模板衍生
# - 游戏实体批量生成
# - 缓存对象快照
# Python 注意事项:
# 1. 浅拷贝共享嵌套对象(陷阱)
# 2. 深拷贝可能很慢/循环引用
# 3. 自定义 __copy__/__deepcopy__ 控制行为
# 自定义深拷贝行为
class MyObj:
def __deepcopy__(self, memo):
# 控制深拷贝逻辑
new = MyObj()
new.simple_field = self.simple_field
return new # 不复制大对象(按需)
优缺点总结
# 优点:
# ✅ 克隆比构建快
# ✅ 动态创建相似对象
# ✅ 与具体类解耦
# 缺点:
# ❌ 深浅拷贝陷阱
# ❌ 深拷贝性能成本
# ❌ 实现 __deepcopy__ 需小心
# 决策:
# 简单复制场景 → copy 模块
# 复杂对象克隆 → 自定义 clone 方法
# 需要覆盖属性 → clone(**overrides) 模式
原型模式用"复制"代替"重建"。掌握深浅拷贝的区别与自定义 clone,复杂对象的"复制改造"游刃有余。
适配器模式
适配器模式(Adapter Pattern)
适配器:将一个类的接口转换成客户端期望的另一个接口,让原本不兼容的类可以协作(类似充电器转接头)。
意图
# 问题: 已有类接口与客户端不匹配
# - 第三方库接口不同
# - 旧系统接口与新系统不一致
# 解决: 适配器包装不兼容接口
# 客户端调用适配器(目标接口)
# 适配器内部转发给被适配者
# 优点:
# - 复用已有类,无需修改
# - 解耦客户端与具体实现
# - 符合开闭原则(加适配器即可)
# 缺点:
# - 增加一层(轻微性能开销)
# - 过度使用让代码绕
结构(对象适配器)
# 参与者:
# Target: 目标接口(客户端期望)
# Adaptee: 被适配者(已有接口)
# Adapter: 适配器(实现 Target,包装 Adaptee)
# Client → Target ← Adapter → Adaptee
Python 示例: 充电器
from abc import ABC, abstractmethod
# 目标接口(客户端期望)
class UsbC(ABC):
@abstractmethod
def charge(self) -> str: ...
# 被适配者(旧接口)
class UsbACharger:
def charge_with_usb_a(self) -> str:
return "使用 USB-A 充电"
# 适配器(把 USB-A 转成 USB-C)
class UsbCAdapter(UsbC):
def __init__(self, charger: UsbACharger):
self.charger = charger
def charge(self) -> str:
# 转发并转换
return f"{self.charger.charge_with_usb_a()} "
f"(通过适配器转 USB-C)"
# 客户端: 只认 UsbC 接口
def charge_phone(charger: UsbC) -> str:
return charger.charge()
old_charger = UsbACharger()
adapter = UsbCAdapter(old_charger)
print(charge_phone(adapter))
# 使用 USB-A 充电 (通过适配器转 USB-C)
类适配器(多继承)
# Python 支持多继承,可做类适配器
class UsbCAdapter2(UsbC, UsbACharger):
def charge(self) -> str:
# 直接调用父类方法
return f"{self.charge_with_usb_a()} (类适配器)"
adapter2 = UsbCAdapter2()
print(charge_phone(adapter2))
# 使用 USB-A 充电 (类适配器)
# 对象适配器(组合) vs 类适配器(继承):
# 对象适配器: 灵活,适配多个对象
# 类适配器: 简单,但继承耦合
实际场景
# 1. 第三方库接口适配
# 例: 两个支付 SDK 接口不同 → 统一适配器
# 2. 旧系统集成
# 例: 旧数据访问接口 → 新 ORM 接口
# 3. 数据格式转换
# 例: XML 数据 → JSON 接口
# 4. 日志库适配
# 例: 统一日志接口 → logging/stdout/第三方
# 示例: 统一支付接口
class PaymentGateway(ABC):
@abstractmethod
def pay(self, amount): ...
class AlipaySDK:
def alipay_pay(self, amount):
return f"支付宝支付 {amount} 元"
class WechatSDK:
def wechat_pay(self, amount):
return f"微信支付 {amount} 元"
class AlipayAdapter(PaymentGateway):
def __init__(self, sdk: AlipaySDK):
self.sdk = sdk
def pay(self, amount):
return self.sdk.alipay_pay(amount)
class WechatAdapter(PaymentGateway):
def __init__(self, sdk: WechatSDK):
self.sdk = sdk
def pay(self, amount):
return self.sdk.wechat_pay(amount)
# 客户端统一调用
def checkout(gateway: PaymentGateway, amount):
return gateway.pay(amount)
print(checkout(AlipayAdapter(AlipaySDK()), 100))
print(checkout(WechatAdapter(WechatSDK()), 100))
Python 动态适配(鸭子类型)
# Python 动态特性: 无需显式实现接口
# 只要方法名一致即可(鸭子类型)
class Duck:
def quack(self): return "嘎嘎"
class Person:
def quack(self): return "学鸭子叫"
# 直接兼容,无需适配器
def make_quack(obj):
return obj.quack()
print(make_quack(Duck()))
print(make_quack(Person()))
# 但接口名不一致时仍需适配器
class Goose:
def honk(self): return "昂昂"
class GooseAdapter:
def __init__(self, goose: Goose):
self.goose = goose
def quack(self):
return f"{self.goose.honk()} (鹅适配为鸭叫)"
print(make_quack(GooseAdapter(Goose())))
优缺点总结
# 优点:
# ✅ 复用已有代码
# ✅ 客户端接口统一
# ✅ 符合开闭原则
# 缺点:
# ❌ 增加抽象层
# ❌ 适配过多时系统复杂
# 决策:
# 接口方法名一致 → 鸭子类型直接用
# 接口不匹配 → 适配器
# 新旧系统长期共存 → 适配器过渡
# 反模式警示:
# 过度适配(每个类都套适配器)会让
# 代码难以理解。优先重构统一接口,
# 适配器作为过渡方案。
适配器是"接口翻译官"。掌握对象/类两种适配方式,配合 Python 鸭子类型,新旧代码无缝协作。
桥接模式
桥接模式(Bridge Pattern)
桥接:将抽象与实现分离,让两者可以独立变化。解决"多维变化"的组合爆炸问题。
意图
# 问题: 两个维度独立变化
# 例: 形状(圆形/方形) × 颜色(红/绿/蓝)
# 继承组合: 3×3=9 个类(爆炸!)
# 解决: 抽象(形状)持有实现(颜色)引用
# 两个维度各自独立扩展
# 优点:
# - 独立扩展两个维度
# - 减少类数量
# - 符合开闭原则(每维加新值只加一个类)
# 缺点:
# - 增加抽象与理解难度
# - 需要正确识别"两个维度"
结构
# 参与者:
# Abstraction: 抽象(高层,持实现引用)
# RefinedAbstraction: 扩展抽象
# Implementor: 实现接口
# ConcreteImplementor: 具体实现
# Shape → Color(桥)
# ├─ Circle ──┐
# └─ Square ──┤
# ├─ Red / Green / Blue
Python 示例: 形状与颜色
from abc import ABC, abstractmethod
# 实现维度: 颜色
class Color(ABC):
@abstractmethod
def apply(self) -> str: ...
class Red(Color):
def apply(self) -> str:
return "红色"
class Green(Color):
def apply(self) -> str:
return "绿色"
# 抽象维度: 形状(持有颜色)
class Shape(ABC):
def __init__(self, color: Color):
self.color = color
@abstractmethod
def draw(self) -> str: ...
class Circle(Shape):
def draw(self) -> str:
return f"画{self.color.apply()}圆形"
class Square(Shape):
def draw(self) -> str:
return f"画{self.color.apply()}方形"
# 组合使用
circle_red = Circle(Red())
square_green = Square(Green())
print(circle_red.draw()) # 画红色圆形
print(square_green.draw()) # 画绿色方形
# 新增"蓝色": 只加 Blue 类,不动形状
# 新增"三角形": 只加 Triangle 类,不动颜色
class Blue(Color):
def apply(self) -> str:
return "蓝色"
print(Circle(Blue()).draw()) # 画蓝色圆形
应用: 跨平台渲染
# 场景: 窗口控件 × 操作系统
# 控件(抽象): 按钮/输入框
# 平台(实现): Windows/Linux/macOS
class Platform(ABC):
@abstractmethod
def render(self, widget) -> str: ...
class Windows(Platform):
def render(self, widget) -> str:
return f"Windows 渲染: {widget}"
class Linux(Platform):
def render(self, widget) -> str:
return f"Linux 渲染: {widget}"
class Widget(ABC):
def __init__(self, platform: Platform):
self.platform = platform
@abstractmethod
def draw(self) -> str: ...
class Button(Widget):
def draw(self) -> str:
return self.platform.render("按钮")
class Input(Widget):
def draw(self) -> str:
return self.platform.render("输入框")
# 组合: 2 控件 × 2 平台 = 4 种组合
# 无需 4 个类!
print(Button(Windows()).draw())
print(Input(Linux()).draw())
# 新增平台只加一个类
class Mac(Platform):
def render(self, widget) -> str:
return f"macOS 渲染: {widget}"
print(Button(Mac()).draw()) # macOS 渲染: 按钮
与适配器对比
# 适配器(Adapter):
# 让"已有接口"适配"期望接口"
# 侧重兼容(解决不匹配)
# 桥接(Bridge):
# 分离"抽象与实现"两个维度
# 侧重解耦(独立变化)
# 对比:
# 适配器: 事后补救(转换接口)
# 桥接: 事前设计(分离维度)
# 选择:
# 接口不兼容 → 适配器
# 多维变化 → 桥接
# 两维度独立扩展 → 桥接
Python 简化
# 动态语言中"抽象接口"可弱化
# 直接鸭子类型即可
class Shape2:
def __init__(self, color): # 任何有 apply 的对象
self.color = color
def draw(self):
return f"画{self.color.apply()}图形"
# 只要 color 有 apply() 方法即可
print(Shape2(Red()).draw())
# 接口也可用 Protocol(类型标注)
from typing import Protocol
class ColorProtocol(Protocol):
def apply(self) -> str: ...
def draw_shape(shape, color: ColorProtocol):
return f"画{color.apply()}{shape}"
优缺点总结
# 优点:
# ✅ 两维度独立扩展(消灭组合爆炸)
# ✅ 减少类数量
# ✅ 开闭原则
# 缺点:
# ❌ 抽象理解成本
# ❌ 需正确识别变化维度
# 决策信号:
# - 组合类爆炸(N×M 类)
# - 两个维度会独立变化
# - 需要运行时切换实现
# 反模式:
# 只有一个变化维度 → 不要用桥接
# 简单场景直接组合即可
桥接是"多维变化"的解药。识别两个独立维度,用组合替代继承,组合爆炸迎刃而解。
过滤器模式
过滤器模式(Filter/Criteria Pattern)
过滤器:用标准(条件)对对象集合进行筛选,支持多个条件自由组合(与/或/非),避免用大量 if-else。
意图
# 问题: 按多个条件筛选集合
# - 条件组合爆炸(if-else 嵌套)
# - 条件复用困难
# 解决: 每个条件是一个"过滤器"
# 过滤器可组合(and/or/not)
# 客户端自由拼装筛选标准
# 优点:
# - 条件独立可复用
# - 组合灵活(标准模式)
# - 符合开闭原则(加条件=加过滤器)
# 缺点:
# - 类数量增加(小场景过度设计)
结构
# 参与者:
# Criteria: 过滤接口(meet(items))
# ConcreteCriteria: 具体条件
# AndCriteria / OrCriteria / NotCriteria: 组合
# Client: 拼装条件并过滤
Python 示例: 人员筛选
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
@dataclass
class Person:
name: str
gender: str
age: int
married: bool = False
# 过滤接口
class Criteria(ABC):
@abstractmethod
def meet(self, items: list[Person]) -> list[Person]:
...
# 具体条件
class MaleCriteria(Criteria):
def meet(self, items):
return [p for p in items if p.gender == "男"]
class AgeCriteria(Criteria):
def __init__(self, min_age, max_age=200):
self.min_age = min_age
self.max_age = max_age
def meet(self, items):
return [p for p in items
if self.min_age <= p.age <= self.max_age]
# 组合条件
class AndCriteria(Criteria):
def __init__(self, *criterias):
self.criterias = criterias
def meet(self, items):
result = items
for c in self.criterias:
result = c.meet(result)
return result
class OrCriteria(Criteria):
def __init__(self, *criterias):
self.criterias = criterias
def meet(self, items):
result = []
for c in self.criterias:
for item in c.meet(items):
if item not in result:
result.append(item)
return result
class NotCriteria(Criteria):
def __init__(self, criteria):
self.criteria = criteria
def meet(self, items):
excluded = set(self.criteria.meet(items))
return [p for p in items if p not in excluded]
使用
people = [
Person("张三", "男", 25),
Person("李四", "女", 30, married=True),
Person("王五", "男", 40, married=True),
Person("赵六", "女", 22),
Person("钱七", "男", 18),
]
male = MaleCriteria()
adult = AgeCriteria(20)
youth = AgeCriteria(18, 25)
# 组合使用
print("成年男性:")
for p in AndCriteria(male, adult).meet(people):
print(" ", p.name)
print("男性或年轻人:")
for p in OrCriteria(male, youth).meet(people):
print(" ", p.name)
print("非男性:")
for p in NotCriteria(male).meet(people):
print(" ", p.name)
print("25-40 岁的男性:")
result = AndCriteria(
male, AgeCriteria(25, 40)).meet(people)
print(" ", [p.name for p in result])
Python 简化: 函数过滤器
# Python 中函数式过滤更自然
from operator import and_, or_
def male(p): return p.gender == "男"
def adult(p): return p.age >= 20
def young(p): return p.age <= 25
# 直接组合(高阶函数)
result = [p for p in people
if male(p) and adult(p)]
# 或用 all/any
def match_all(p, *preds):
return all(pred(p) for pred in preds)
def match_any(p, *preds):
return any(pred(p) for pred in preds)
# 使用
result = [p for p in people
if match_all(p, male, adult)]
result2 = [p for p in people
if match_any(p, male, young)]
# 或 functools 组合
import functools, operator
result3 = [p for p in people
if functools.reduce(and_,
[male(p), adult(p)])]
# 复杂组合才用类(标准)方式
# 简单条件用函数/Python 内置更清晰
适用场景
# 1. 多条件筛选(搜索/列表过滤)
# 2. 规则引擎(条件组合匹配)
# 3. 数据校验(多个校验规则组合)
# 4. 权限检查(多个条件)
# 5. 报表过滤(多维度筛选)
# 实际案例:
# - 商品筛选(价格/分类/品牌/评价)
# - 用户搜索(地区/年龄/活跃度)
# - 日志过滤(级别/来源/关键词)
# 与"职责链"区分:
# 过滤器: 筛选集合(输出子集)
# 责任链: 处理请求(依次处理)
# 与"策略"区分:
# 过滤器: 组合筛选条件
# 策略: 替换整个算法
优缺点总结
# 优点:
# ✅ 条件独立可复用
# ✅ 自由组合(与/或/非)
# ✅ 开闭原则
# ✅ 清晰可读
# 缺点:
# ❌ 类数量增加
# ❌ 简单过滤过度设计
# 决策:
# 简单条件 → 推导式/Python 内置
# 复杂组合且需复用 → 过滤器模式
# 动态组合规则 → 过滤器类(可序列化)
# 最佳实践:
# - 先函数式,再按需升级为类
# - 组合逻辑清晰命名(And/Or/Not)
# - 过滤条件保持纯函数(易测试)
过滤器把"条件组合"结构化。掌握与/或/非组合,配合 Python 函数式风格,筛选逻辑灵活清晰。
组合模式
组合模式(Composite Pattern)
组合:将对象组织成树形结构,让"部分-整体"的处理一致。客户端无需区分叶子与分支,统一调用。
意图
# 问题: 树形结构(目录/菜单/组织架构)
# - 处理叶子与容器逻辑不同
# - 客户端需判断类型
# 解决: 统一接口(叶子与容器都实现)
# 容器持有子节点,操作递归传播
# 优点:
# - 客户端统一处理(无需 if 判断)
# - 新增节点类型容易
# - 树形遍历自然递归
# 缺点:
# - 接口过度统一(叶子实现无关方法)
# - 可能破坏单一职责
结构
# 参与者:
# Component: 组件接口(叶子与容器共有)
# Leaf: 叶子(无子节点)
# Composite: 容器(含子节点列表)
# 例: 文件系统
# Folder(Composite) → File(Leaf)
# 删除文件夹 = 递归删除所有内容
Python 示例: 文件系统
from abc import ABC, abstractmethod
# 组件接口
class FileSystemNode(ABC):
@abstractmethod
def get_size(self) -> int: ...
def display(self, indent: str = ""): ...
# 叶子: 文件
class File(FileSystemNode):
def __init__(self, name, size):
self.name = name
self.size = size
def get_size(self) -> int:
return self.size
def display(self, indent=""):
print(f"{indent}📄 {self.name} ({self.size}B)")
# 容器: 文件夹
class Folder(FileSystemNode):
def __init__(self, name):
self.name = name
self.children = []
def add(self, node: FileSystemNode):
self.children.append(node)
return self
def remove(self, node: FileSystemNode):
self.children.remove(node)
def get_size(self) -> int:
# 递归求和(统一接口!)
return sum(c.get_size() for c in self.children)
def display(self, indent=""):
print(f"{indent}📁 {self.name}/")
for child in self.children:
child.display(indent + " ")
使用
# 构建目录树
root = Folder("root")
docs = Folder("docs")
docs.add(File("readme.md", 100))
docs.add(File("guide.md", 250))
src = Folder("src")
src.add(File("main.py", 800))
src.add(File("utils.py", 400))
tests = Folder("tests")
tests.add(File("test_main.py", 300))
src.add(tests) # 嵌套
root.add(docs)
root.add(src)
# 统一接口: 递归显示
root.display()
# 📁 root/
# 📁 docs/
# 📄 readme.md (100B)
# 📄 guide.md (250B)
# 📁 src/
# 📄 main.py (800B)
# 📄 utils.py (400B)
# 📁 tests/
# 📄 test_main.py (300B)
# 统一获取大小(递归)
print(f"总大小: {root.get_size()}B")
# 总大小: 1850B
# 客户端无需判断类型!
def total_size(node: FileSystemNode):
return node.get_size()
print(total_size(src)) # 1500
print(total_size(docs)) # 350
应用: 菜单系统
# 菜单项(叶子)与子菜单(容器)
class MenuItem(FileSystemNode): # 复用结构
...
# 实际应用:
# - 菜单/导航树
# - 组织架构树
# - 表达式树
# - UI 组件树(容器与控件统一)
# - HTML DOM 树
# UI 组件示例
class UIComponent(ABC):
@abstractmethod
def render(self): ...
class Button(UIComponent):
def render(self):
return ""
class Panel(UIComponent):
def __init__(self):
self.components = []
def add(self, comp):
self.components.append(comp)
def render(self):
inner = "".join(c.render()
for c in self.components)
return f"{inner}"
page = Panel()
page.add(Button())
page.add(Panel().add(Button()))
print(page.render())
#
安全与透明
# 透明方式: 接口统一(叶子也有 add 方法)
# - 客户端无感知,但叶子 add 应报错
# 安全方式: 只有容器有 add 方法
# - 客户端需判断类型(违背组合意图)
# Python 中权衡:
# 接口统一(get_size 等)是核心
# add/remove 可只在容器实现(鸭子类型)
# 避免循环引用:
# 添加子节点时检查是否自身
def add(self, node):
if node is self:
raise ValueError("不能添加自身")
self.children.append(node)
优缺点总结
# 优点:
# ✅ 统一处理部分-整体
# ✅ 树形递归遍历
# ✅ 新增节点类型易
# 缺点:
# ❌ 接口过度统一
# ❌ 可能隐藏类型差异
# 决策:
# 树形结构且需统一操作 → 组合
# 简单层级 → 直接用列表嵌套
# Python 简化:
# 用 dict/list 嵌套即可表达树
tree = {
"name": "root",
"children": [
{"name": "file1", "size": 100},
{"name": "dir", "children": [...]},
],
}
# 递归函数处理(函数式替代)
def calc_size(node):
return node.get("size", 0) + sum(
calc_size(c)
for c in node.get("children", []))
组合模式让"树形结构"处理统一。掌握递归接口设计,目录/菜单/组件树类场景自然优雅。
装饰器模式
装饰器模式(Decorator Pattern)
装饰器:动态地给对象添加职责,比继承更灵活。Python 的 @decorator 语法就是这一思想的语言级实现。
意图
# 问题: 需要动态添加功能
# - 继承方式: 功能组合爆炸(加糖+加冰+大杯...)
# - 修改原类: 违反开闭原则
# 解决: 包装器(装饰器)层层包裹
# 每个装饰器实现同一接口,内部调用被包装对象
# 优点:
# - 动态组合功能
# - 替代继承(组合优于继承)
# - 符合开闭原则
# 缺点:
# - 多层级包装调试难
# - 对象标识变化(== 比较可能失效)
结构
# 参与者:
# Component: 组件接口
# ConcreteComponent: 具体组件(被装饰)
# Decorator: 装饰器(持组件引用,实现接口)
# ConcreteDecorator: 具体装饰器(添加职责)
# Coffee → MilkDecorator → SugarDecorator
# 层层包裹,叠加功能
Python 示例: 咖啡订单
from abc import ABC, abstractmethod
# 组件接口
class Beverage(ABC):
@abstractmethod
def cost(self) -> float: ...
@abstractmethod
def description(self) -> str: ...
# 具体组件
class Espresso(Beverage):
def cost(self) -> float:
return 15.0
def description(self) -> str:
return "浓缩咖啡"
# 装饰器基类
class CondimentDecorator(Beverage):
def __init__(self, beverage: Beverage):
self.beverage = beverage
# 具体装饰器
class Milk(CondimentDecorator):
def cost(self) -> float:
return self.beverage.cost() + 3.0
def description(self) -> str:
return self.beverage.description() + ", 加奶"
class Sugar(CondimentDecorator):
def cost(self) -> float:
return self.beverage.cost() + 1.5
def description(self) -> str:
return self.beverage.description() + ", 加糖"
class Whip(CondimentDecorator):
def cost(self) -> float:
return self.beverage.cost() + 5.0
def description(self) -> str:
return self.beverage.description() + ", 加奶油"
使用: 动态组合
# 基础咖啡
coffee = Espresso()
print(coffee.description(), coffee.cost())
# 浓缩咖啡 15.0
# 加奶
coffee = Milk(coffee)
print(coffee.description(), coffee.cost())
# 浓缩咖啡, 加奶 18.0
# 再加糖和奶油
coffee = Sugar(Whip(coffee))
print(coffee.description(), coffee.cost())
# 浓缩咖啡, 加奶, 加奶油, 加糖 24.5
# 不同的组合随意搭配
another = Sugar(Espresso()) # 只要糖
print(another.description(), another.cost())
# 浓缩咖啡, 加糖 16.5
# 新增"豆浆": 只需一个类,无需修改咖啡类
class Soy(CondimentDecorator):
def cost(self) -> float:
return self.beverage.cost() + 2.0
def description(self) -> str:
return self.beverage.description() + ", 加豆浆"
Python 装饰器语法
# Python 语言级装饰器(函数装饰)
import time
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} 耗时 {elapsed:.4f}s")
return result
return wrapper
@timer # 等价 slow = timer(slow)
def slow():
time.sleep(0.1)
slow()
# slow 耗时 0.1001s
# 多层装饰(叠加功能)
def log(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__}")
return func(*args, **kwargs)
return wrapper
@timer
@log
def process(x):
return x * 2
process(5)
# 调用 process
# process 耗时 0.0000s
实际应用
# 1. IO 流包装
# - BufferedInputStream → FileInputStream
# - gzip 压缩流 → 文件流
# 2. 中间件/拦截器(Web)
# - 认证 → 日志 → 缓存 → 业务处理
# 3. 函数增强(Python 装饰器)
# - @staticmethod / @classmethod
# - @property
# - @lru_cache(缓存)
from functools import lru_cache
@lru_cache(maxsize=128)
def fib(n):
return n if n < 2 else fib(n-1) + fib(n-2)
# 4. 数据增强管道
# - 压缩 → 加密 → 序列化
# 5. 权限/校验包装
与代理模式对比
# 装饰器(Decorator):
# 添加新功能(增强)
# 接口不变,功能叠加
# 代理(Proxy):
# 控制访问(延迟/权限/日志)
# 关注访问控制,非增强
# 对比:
# 装饰器: 客户端直接构造包装链
# 代理: 客户端通常不知代理存在
# 选择:
# 动态加功能 → 装饰器
# 控制访问 → 代理
优缺点总结
# 优点:
# ✅ 动态组合功能
# ✅ 优于继承(组合)
# ✅ 开闭原则
# ✅ Python 语法原生支持
# 缺点:
# ❌ 多层包装调试困难
# ❌ 包装顺序影响行为(需注意)
# ❌ 对象比较/类型判断变复杂
# 最佳实践:
# - 装饰器保持"透明"(接口不变)
# - 单个装饰器职责单一
# - 注意装饰顺序(从内到外执行)
# - 用 functools.wraps 保留元数据
# Python 建议:
# 类装饰 → 继承 Decorator 基类
# 函数增强 → @decorator 语法
# 简单场景 → 手动包装函数
装饰器是"动态增强"的利器,Python 语法原生支持。掌握类装饰与 @decorator,功能叠加灵活轻量。
外观模式
外观模式(Facade Pattern)
外观:为复杂的子系统提供一个统一的高层接口,让客户端简单使用。相当于"前台/门面"。
意图
# 问题: 子系统接口复杂(多个类互相协作)
# - 客户端需要了解内部细节
# - 调用步骤繁琐易错
# 解决: 外观类封装复杂流程
# 客户端只调用外观的简单方法
# 优点:
# - 简化客户端(降低使用门槛)
# - 解耦客户端与子系统
# - 隐藏内部细节
# 缺点:
# - 外观可能成为"上帝对象"
# - 过度封装损失灵活性
# - 外观与子系统耦合
结构
# 参与者:
# Facade: 外观(提供简化接口)
# Subsystems: 子系统(复杂内部逻辑)
# Client → Facade → 各 Subsystem
# 客户端不直接碰子系统
Python 示例: 家庭影院
# 子系统
class Projector:
def on(self): return "投影仪开机"
def off(self): return "投影仪关机"
def set_input(self, src): return f"切换到 {src}"
class SoundSystem:
def on(self): return "音响开机"
def set_volume(self, v): return f"音量调到 {v}"
def off(self): return "音响关机"
class DVDPlayer:
def play(self, movie): return f"播放 {movie}"
def stop(self): return "停止播放"
def eject(self): return "弹出光盘"
class Lights:
def dim(self, level): return f"灯光调暗到 {level}%"
def bright(self): return "灯光调亮"
# 外观
class HomeTheaterFacade:
def __init__(self):
self.projector = Projector()
self.sound = SoundSystem()
self.dvd = DVDPlayer()
self.lights = Lights()
def watch_movie(self, movie: str) -> list[str]:
"""一键看电影(封装复杂流程)"""
steps = []
steps.append(self.lights.dim(30))
steps.append(self.projector.on())
steps.append(self.projector.set_input("DVD"))
steps.append(self.sound.on())
steps.append(self.sound.set_volume(50))
steps.append(self.dvd.play(movie))
return steps
def end_movie(self) -> list[str]:
steps = []
steps.append(self.dvd.stop())
steps.append(self.dvd.eject())
steps.append(self.projector.off())
steps.append(self.sound.off())
steps.append(self.lights.bright())
return steps
# 客户端: 只需两步
theater = HomeTheaterFacade()
for step in theater.watch_movie("星际穿越"):
print(step)
# 灯光调暗到 30%
# 投影仪开机
# 切换到 DVD
# 音响开机
# 音量调到 50
# 播放 星际穿越
for step in theater.end_movie():
print(step)
应用: 数据访问门面
# 场景: 订单系统(用户+库存+支付+通知)
class UserService:
def check(self, uid):
return f"校验用户 {uid}"
class InventoryService:
def deduct(self, pid, qty):
return f"扣减库存 {pid} x{qty}"
class PaymentService:
def charge(self, uid, amount):
return f"扣款 {amount}"
class NotifyService:
def send(self, uid, msg):
return f"通知 {uid}: {msg}"
# 外观: 下单门面
class OrderFacade:
def __init__(self):
self.users = UserService()
self.inventory = InventoryService()
self.payment = PaymentService()
self.notify = NotifyService()
def place_order(self, uid, pid, qty, amount):
"""下单完整流程(封装)"""
steps = []
steps.append(self.users.check(uid))
steps.append(self.inventory.deduct(pid, qty))
steps.append(self.payment.charge(uid, amount))
steps.append(self.notify.send(
uid, f"订单成功,金额 {amount}"))
return steps
# 客户端一行调用
facade = OrderFacade()
print(facade.place_order(1, "P001", 2, 199.9))
# 好处: 客户端不关心内部服务如何协作
# 修改内部流程不影响客户端
与适配器/门面对比
# 适配器(Adapter): 转换接口(1:1)
# 门面(Facade): 简化多个接口(1:N)
# 对比:
# 适配器: 让接口匹配(兼容)
# 门面: 让接口简单(封装)
# 应用场景区分:
# 接口不匹配 → 适配器
# 流程复杂 → 门面
# 门面 vs 中间件:
# 门面: 客户端主动调用
# 中间件: 请求自动经过
# Python 中门面常表现为:
# - 服务类的统一入口
# - 高层 API 封装
# - __init__.py 的导出接口
优缺点总结
# 优点:
# ✅ 简化客户端使用
# ✅ 解耦(客户端与子系统)
# ✅ 隐藏内部细节
# ✅ 降低学习成本
# 缺点:
# ❌ 外观可能臃肿
# ❌ 客户端失去底层灵活性
# ❌ 与子系统耦合
# 最佳实践:
# - 外观保持"薄"(只做编排)
# - 提供多个粒度的外观方法
# - 不把业务逻辑塞进外观
# - 必要时暴露子系统(高级用户)
# 决策:
# 子系统复杂且客户端频繁使用 → 门面
# 简单的几个类 → 不需要门面
外观是"复杂系统的前台"。掌握封装编排与粒度控制,复杂流程对客户端始终简单清晰。
享元模式
享元模式(Flyweight Pattern)
享元:共享对象以减少内存占用。将对象的"内在状态"(可共享)与"外在状态"(随场景变化)分离。
意图
# 问题: 大量相似对象占用内存
# 例: 文档中 10 万个字符对象
# 解决: 共享"可复用的部分"(享元)
# 每个对象只保存"独有部分"
# 内在状态: 可共享(字符的形状)
# 外在状态: 场景特有(字符的位置)
# 优点:
# - 大幅减少内存
# - 对象复用
# 缺点:
# - 复杂度增加
# - 需要区分内在外在状态
# - 线程安全需注意
结构
# 参与者:
# Flyweight: 享元接口
# ConcreteFlyweight: 具体享元(内在状态)
# FlyweightFactory: 享元工厂(缓存复用)
# Client: 使用享元(管理外在状态)
# 例: 字符渲染
# 字符 glyph(内在) + 位置/颜色(外在)
Python 示例: 字符渲染
class Glyph: # 享元(字符形状,内在状态)
def __init__(self, char: str, font: str):
self.char = char
self.font = font
def render(self, x, y, color):
# 外在状态(x, y, color)由调用方传入
return (f"[{self.char}/{self.font} "
f"在({x},{y}) 颜色{color}]")
# 享元工厂: 缓存复用
class GlyphFactory:
def __init__(self):
self._pool = {}
def get_glyph(self, char: str,
font: str = "default") -> Glyph:
key = (char, font)
if key not in self._pool:
self._pool[key] = Glyph(char, font)
return self._pool[key]
@property
def pool_size(self):
return len(self._pool)
# 使用
factory = GlyphFactory()
text = "Hello World Hello World"
positions = []
for i, ch in enumerate(text):
glyph = factory.get_glyph(ch)
positions.append(glyph.render(
i % 10, i // 10, "black"))
print(f"字符数: {len(text)}") # 22
print(f"享元数: {factory.pool_size}") # 8(去重后)
# 只有 8 个唯一字符对象被创建!
应用: 粒子系统/地图标记
# 游戏粒子系统(大量粒子)
class ParticleType: # 享元: 粒子外观(纹理等)
def __init__(self, color, texture):
self.color = color
self.texture = texture
class ParticleFactory:
_types = {}
@classmethod
def get_type(cls, color, texture):
key = (color, texture)
if key not in cls._types:
cls._types[key] = ParticleType(
color, texture)
return cls._types[key]
# 单个粒子(外在状态多)
class Particle:
def __init__(self, x, y, speed,
ptype: ParticleType):
self.x, self.y = x, y
self.speed = speed
self.type = ptype # 共享类型
# 1 万粒子,但只有几种类型
factory = ParticleFactory
particles = []
for i in range(10000):
ptype = factory.get_type(
"red" if i % 2 else "blue", "circle")
particles.append(Particle(
i % 100, i % 100, i % 10, ptype))
print(f"粒子数: {len(particles)}")
print(f"类型数: {len(ParticleFactory._types)}")
# 类型数: 2(共享,内存大幅节省)
# 其他应用:
# - 文本编辑器字符对象
# - 地图城市标记(共享图标)
# - 数据库连接池(复用连接)
# - 字符串驻留(interning)
与单例/对象池对比
# 单例(Singleton): 全局一个实例
# 享元(Flyweight): 一组可共享实例
# 对象池(Pool): 复用创建成本高的对象
# 对比:
# 享元: 按"内在状态"区分共享
# 对象池: 临时借用与归还(连接池)
# 选择:
# 对象重复且内在状态可共享 → 享元
# 对象创建贵但需独占使用 → 对象池
# Python 内置享元思想:
# - 小整数驻留(-5~256 缓存)
# - 字符串驻留(短字符串复用)
# - lru_cache(函数结果复用)
优缺点总结
# 优点:
# ✅ 大幅减少内存
# ✅ 提升性能(少创建)
# ✅ 复用对象
# 缺点:
# ❌ 复杂度增加(内外状态分离)
# ❌ 状态管理需谨慎
# ❌ 共享对象修改风险
# 最佳实践:
# - 内在状态只读(不可变)
# - 工厂集中管理共享对象
# - 外在状态不存享元内
# - 大数据量场景评估收益
# 决策:
# 对象数量大且重复 → 享元
# 数量小 → 直接创建
# Python: 优先用不可变数据结构
# (str/int/tuple 天然共享)
享元用"共享"对抗"重复"。掌握内外状态分离与工厂缓存,海量相似对象的内存压力迎刃而解。
代理模式
代理模式(Proxy Pattern)
代理:为对象提供一个替身(代理)来控制对它的访问。可在访问前后增加逻辑(权限/缓存/日志/延迟加载)。
意图
# 问题: 直接访问对象不安全/低效/不便
# - 大对象不想立即加载
# - 需要权限控制
# - 需要记录访问日志
# 解决: 代理实现同一接口,控制真实对象的访问
# 优点:
# - 控制访问(权限/日志)
# - 延迟加载(虚拟代理)
# - 远程访问(远程代理)
# - 符合开闭原则
# 缺点:
# - 增加一层
# - 响应可能延迟(代理逻辑)
类型
# 虚拟代理: 延迟创建昂贵对象
# 保护代理: 控制访问权限
# 缓存代理: 缓存结果
# 远程代理: 本地替身访问远程
# 日志代理: 记录访问
# 结构与装饰器相似
# 区别: 代理控制访问,装饰器增加功能
Python 示例: 虚拟代理(延迟加载)
from abc import ABC, abstractmethod
# 主题接口
class Image(ABC):
@abstractmethod
def display(self) -> str: ...
# 真实对象(创建昂贵)
class RealImage(Image):
def __init__(self, filename):
self.filename = filename
self._load_from_disk() # 昂贵操作
def _load_from_disk(self):
print(f"从磁盘加载: {self.filename}")
def display(self) -> str:
return f"显示图片: {self.filename}"
# 虚拟代理: 延迟到真正需要时创建
class ImageProxy(Image):
def __init__(self, filename):
self.filename = filename
self._real = None # 尚未加载
def display(self) -> str:
if self._real is None:
self._real = RealImage(self.filename)
return self._real.display()
# 客户端: 与使用 RealImage 一样
proxy = ImageProxy("photo.jpg")
# (此时未加载)
print(proxy.display()) # 首次: 触发加载
# 从磁盘加载: photo.jpg
# 显示图片: photo.jpg
print(proxy.display()) # 再次: 复用
# 显示图片: photo.jpg
保护代理(权限控制)
class Document:
def __init__(self, content):
self.content = content
def read(self) -> str:
return self.content
def edit(self, new_content):
self.content = new_content
return "已编辑"
# 保护代理
class SecureDocumentProxy:
def __init__(self, doc: Document,
role: str):
self.doc = doc
self.role = role
def read(self) -> str:
# 任何人可读
return self.doc.read()
def edit(self, new_content):
# 只有管理员可编辑
if self.role != "admin":
raise PermissionError("无编辑权限")
return self.doc.edit(new_content)
doc = Document("机密文档")
user_proxy = SecureDocumentProxy(doc, "user")
admin_proxy = SecureDocumentProxy(doc, "admin")
print(user_proxy.read()) # 机密文档
# user_proxy.edit("x") # PermissionError!
print(admin_proxy.edit("新内容")) # 已编辑
缓存代理
class ExpensiveService:
def fetch(self, key):
# 模拟昂贵查询
print(f"查询: {key}")
return {"key": key, "data": "..."}
# 缓存代理
class CachedProxy:
def __init__(self, service):
self.service = service
self.cache = {}
def fetch(self, key):
if key not in self.cache:
self.cache[key] = self.service.fetch(key)
return self.cache[key]
service = ExpensiveService()
proxy = CachedProxy(service)
print(proxy.fetch("a")) # 查询: a(实际查询)
print(proxy.fetch("a")) # (缓存命中,不查询)
print(proxy.fetch("b")) # 查询: b
# 注意: 过期策略/一致性
# Python 中 @lru_cache 即缓存代理
from functools import lru_cache
@lru_cache(maxsize=128)
def fetch(key):
print(f"查询: {key}")
return {"key": key}
Python 中的代理简化
# 函数代理(装饰器实现)
def cached(func):
cache = {}
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@cached
def heavy(n):
print(f"计算: {n}")
return n * n
# 属性访问代理
class LazyAttribute:
def __get__(self, obj, owner):
# 延迟计算的描述符(虚拟代理)
print("计算属性值...")
value = expensive_compute()
obj.__dict__[self.name] = value
return value
# 远程代理示例:
# requests 库/ORM 查询(惰性)
# 数据库 QuerySet 延迟求值
优缺点总结
# 优点:
# ✅ 控制访问(权限/日志)
# ✅ 延迟加载(性能)
# ✅ 缓存(性能)
# ✅ 解耦(客户端与真实对象)
# 缺点:
# ❌ 增加一层
# ❌ 代理逻辑复杂化
# ❌ 需要维护代理与真实的同步
# 决策:
# 延迟加载 → 虚拟代理
# 权限控制 → 保护代理
# 缓存 → 缓存代理/lru_cache
# 远程调用 → 远程代理(如 RPC 客户端)
# 反模式:
# 代理与真实对象不一致
# 无实际需求的代理(徒增复杂度)
代理是"对象的守门人"。掌握虚拟/保护/缓存代理,延迟加载、权限控制、性能优化一网打尽。
责任链模式
责任链模式(Chain of Responsibility)
责任链:将请求沿着处理者链传递,每个处理者决定处理或传给下一个。解耦"发送者"与"接收者"。
意图
# 问题: 请求处理逻辑分散且可能变化
# - 硬编码 if-else 判断处理者
# - 处理顺序/处理者变更困难
# 解决: 处理者链,请求依次传递
# 每个处理者: 能处理就处理,否则传给下一个
# 优点:
# - 解耦发送者与接收者
# - 动态调整链(顺序/增删)
# - 符合开闭原则
# 缺点:
# - 请求可能未被处理(需兜底)
# - 调试链式传递较难
# - 性能(长链开销)
结构
# 参与者:
# Handler: 处理者接口(设 next + handle)
# ConcreteHandler: 具体处理者
# Client: 构建链并发送请求
# 例: 审批流程
# 员工请假 → 组长 → 经理 → 总监 → 拒绝
Python 示例: 审批流程
from abc import ABC, abstractmethod
class Approver(ABC):
def __init__(self):
self.next = None
def set_next(self, approver):
self.next = approver
return approver # 链式设置
@abstractmethod
def approve(self, amount: float) -> str: ...
class TeamLeader(Approver):
def approve(self, amount):
if amount <= 1000:
return f"组长批准: {amount} 元"
return (f"组长无权,转交"
f"{self.next.approve(amount)}")
class Manager(Approver):
def approve(self, amount):
if amount <= 5000:
return f"经理批准: {amount} 元"
return (f"经理无权,转交"
f"{self.next.approve(amount)}")
class Director(Approver):
def approve(self, amount):
if amount <= 20000:
return f"总监批准: {amount} 元"
return f"超出权限,拒绝: {amount} 元"
# 构建链
leader = TeamLeader()
manager = Manager()
director = Director()
leader.set_next(manager).set_next(director)
# 发送请求(客户端只认识链头)
print(leader.approve(500))
# 组长批准: 500 元
print(leader.approve(3000))
# 组长无权,转交经理批准: 3000 元
print(leader.approve(8000))
# 组长无权,转交经理无权,转交总监批准: 8000 元
print(leader.approve(50000))
# ... 拒绝: 50000 元
# 动态调整链(加角色/改顺序)
class Boss(Approver):
def approve(self, amount):
return f"老板批准: {amount} 元"
leader.set_next(manager).set_next(
director).set_next(Boss())
print(leader.approve(50000))
# ... 老板批准: 50000 元
应用: 中间件/过滤器链
# Web 中间件就是责任链!
# 请求 → 认证 → 日志 → 限流 → 业务
class Middleware:
def __init__(self):
self.next = None
def set_next(self, mw):
self.next = mw
return mw
def handle(self, request):
if self.next:
return self.next.handle(request)
return "处理完成"
class AuthMiddleware(Middleware):
def handle(self, request):
if not request.get("token"):
return "401 未认证"
return super().handle(request)
class LoggerMiddleware(Middleware):
def handle(self, request):
print(f"日志: {request.get('path')}")
return super().handle(request)
# 构建中间件链
chain = AuthMiddleware()
chain.set_next(LoggerMiddleware())
print(chain.handle({"path": "/api", "token": None}))
# 401 未认证
print(chain.handle({"path": "/api", "token": "abc"}))
# 日志: /api
# 处理完成
# 框架中的责任链:
# - Django 中间件(MIDDLEWARE 列表)
# - Express/Koa 中间件
# - 表单校验链
# - 事件冒泡(UI)
Python 简化
# 函数列表式责任链
handlers: list = []
def add_handler(fn):
handlers.append(fn)
return fn
@add_handler
def check_auth(req):
if not req.get("token"):
return "401"
return None # 不处理,继续
@add_handler
def check_rate(req):
if req.get("burst"):
return "429"
return None
def process(req):
for handler in handlers:
result = handler(req)
if result:
return result
return "OK"
print(process({"token": None})) # 401
print(process({"token": "x"})) # OK
print(process({"token": "x", "burst": True})) # 429
# 或使用哨兵/异常控制流(谨慎)
优缺点总结
# 优点:
# ✅ 解耦发送与处理
# ✅ 动态调整链
# ✅ 开闭原则(加处理者即可)
# ✅ 职责单一(每个处理者一个职责)
# 缺点:
# ❌ 请求可能无人处理
# ❌ 调试困难(链式)
# ❌ 长链性能开销
# 最佳实践:
# - 链尾加兜底处理者
# - 处理者职责单一、顺序清晰
# - 控制链长度
# - 日志记录传递过程(调试)
# 决策:
# 多个条件依次处理 → 责任链
# 组合条件筛选 → 过滤器
# 简单 if-else → 不需要模式
责任链让"请求处理"灵活传递。掌握链式构建与兜底,审批流、中间件、校验链信手拈来。
命令模式
命令模式(Command Pattern)
命令:将请求封装为对象,从而支持参数化、排队、记录日志与撤销操作。
意图
# 问题: 请求直接绑定到执行者
# - 难以撤销/重做
# - 难以排队/记录
# - 发送者与接收者耦合
# 解决: 请求封装为命令对象
# 命令对象: 接收者 + 执行方法
# 调用方只发命令,不知接收者细节
# 优点:
# - 撤销/重做(保存命令历史)
# - 队列/延迟执行
# - 日志与审计
# - 发送与执行解耦
# 缺点:
# - 类数量增加
# - 简单调用过度设计
结构
# 参与者:
# Command: 命令接口(execute)
# ConcreteCommand: 具体命令(持接收者)
# Receiver: 接收者(实际执行)
# Invoker: 调用者(发命令)
# Client: 组装
# 例: 遥控器
# 按钮(Invoker) → 开灯命令(Command) → 灯(Receiver)
Python 示例: 文本编辑器
from abc import ABC, abstractmethod
# 接收者
class TextEditor:
def __init__(self):
self.text = ""
def insert(self, text, pos=None):
self.text += text
def delete(self, count):
removed = self.text[-count:]
self.text = self.text[:-count]
return removed
# 命令接口
class Command(ABC):
@abstractmethod
def execute(self): ...
@abstractmethod
def undo(self): ...
# 具体命令: 插入
class InsertCommand(Command):
def __init__(self, editor, text):
self.editor = editor
self.text = text
def execute(self):
self.editor.insert(self.text)
def undo(self):
self.editor.delete(len(self.text))
# 具体命令: 删除
class DeleteCommand(Command):
def __init__(self, editor, count):
self.editor = editor
self.count = count
self._removed = ""
def execute(self):
self._removed = self.editor.delete(
self.count)
def undo(self):
self.editor.insert(self._removed)
# 调用者(Invoker): 管理命令历史
class CommandHistory:
def __init__(self):
self.history = []
self.editor = TextEditor()
def execute(self, command: Command):
command.execute()
self.history.append(command)
def undo(self):
if self.history:
cmd = self.history.pop()
cmd.undo()
# 使用
app = CommandHistory()
app.execute(InsertCommand(app.editor, "Hello"))
app.execute(InsertCommand(app.editor, " World"))
print(app.editor.text) # Hello World
app.execute(DeleteCommand(app.editor, 6))
print(app.editor.text) # Hello
app.undo() # 撤销删除
print(app.editor.text) # Hello World
app.undo() # 撤销插入
print(app.editor.text) # Hello
app.undo() # 撤销插入
print(app.editor.text) # (空)
# 撤销/重做栈:
# undo_stack / redo_stack 双栈实现重做
Python 简化: 函数命令
# Python 中函数即可作命令
class Editor:
def __init__(self):
self.text = ""
self.history = []
def execute(self, action, undo_action):
action()
self.history.append(undo_action)
def undo(self):
if self.history:
self.history.pop()()
editor = Editor()
# 命令就是闭包/函数
editor.execute(
lambda: editor.text.__add__("a"),
# 简化示意
)
# 更清晰: 用函数封装
def insert(s):
editor.text += s
def undo():
editor.text = editor.text[:-len(s)]
return undo
# 命令 = 函数 + 撤销函数
cmds = []
cmds.append(insert("Hello"))
# 执行
for cmd in cmds:
cmd()
# 用类型别名组织
from typing import Callable
CommandFn = Callable[[], None]
应用场景
# 1. 撤销/重做(编辑器/画板)
# 2. 任务队列(延迟/批处理)
# 3. 宏命令(组合多个命令)
# 4. 事务(全部成功或回滚)
# 5. UI 按钮/菜单(解耦)
# 6. 网络请求封装(可重试)
# 实际案例:
# - 数据库事务(提交/回滚)
# - IDE 操作历史(撤销栈)
# - 游戏操作(回放)
# - 分布式任务调度
# 宏命令(组合)
class MacroCommand(Command):
def __init__(self, commands):
self.commands = commands
def execute(self):
for c in self.commands:
c.execute()
def undo(self):
for c in reversed(self.commands):
c.undo()
优缺点总结
# 优点:
# ✅ 撤销/重做
# ✅ 队列/延迟/日志
# ✅ 解耦发送与执行
# ✅ 组合命令(宏)
# 缺点:
# ❌ 类数量增加
# ❌ 简单调用过度设计
# 决策:
# 需要撤销/队列 → 命令模式
# 简单调用 → 直接调用
# Python: 函数/闭包可作轻量命令
# 最佳实践:
# - 命令保持小且单一
# - 撤销逻辑与执行对称
# - 历史栈限长(防内存)
# - 组合命令(宏)复用
命令模式把"请求"变成"对象"。掌握执行/撤销与历史栈,编辑器、队列、事务类需求从容应对。
解释器模式
解释器模式(Interpreter Pattern)
解释器:为语言定义文法表示并解释语句。适合定义"规则/表达式"并求值的场景。
意图
# 问题: 需要解析并执行"规则/表达式"
# - 配置规则(如 "age > 18 AND active")
# - 简单脚本/查询语言
# 解决: 表达式树 + 解释执行
# 每个文法规则对应一个类
# 优点:
# - 易于扩展文法(加规则=加类)
# - 表达式树结构清晰
# 缺点:
# - 类数量膨胀(规则多时)
# - 复杂文法用解释器不划算
# (应改用解析器库: lark/pyparsing)
结构
# 参与者:
# AbstractExpression: 抽象表达式(interpret)
# TerminalExpression: 终结符(变量/常量)
# NonterminalExpression: 非终结符(运算组合)
# Context: 上下文(变量值等)
# 例: 算术表达式
# 1 + 2 * 3 → 表达式树
# +
# / \
# 1 *
# / \
# 2 3
Python 示例: 数学表达式
from abc import ABC, abstractmethod
# 抽象表达式
class Expression(ABC):
@abstractmethod
def interpret(self, context: dict) -> int: ...
# 终结符: 数字
class Number(Expression):
def __init__(self, value: int):
self.value = value
def interpret(self, context):
return self.value
# 终结符: 变量
class Variable(Expression):
def __init__(self, name: str):
self.name = name
def interpret(self, context):
return context.get(self.name, 0)
# 非终结符: 二元运算
class Add(Expression):
def __init__(self, left, right):
self.left, self.right = left, right
def interpret(self, context):
return (self.left.interpret(context)
+ self.right.interpret(context))
class Multiply(Expression):
def __init__(self, left, right):
self.left, self.right = left, right
def interpret(self, context):
return (self.left.interpret(context)
* self.right.interpret(context))
# 构建表达式: (x + 5) * 2
expr = Multiply(
Add(Variable("x"), Number(5)),
Number(2),
)
# 解释执行
print(expr.interpret({"x": 10})) # 30
print(expr.interpret({"x": 3})) # 16
# 更复杂: ((x + 1) * (y - 2))
class Subtract(Expression):
def __init__(self, left, right):
self.left, self.right = left, right
def interpret(self, context):
return (self.left.interpret(context)
- self.right.interpret(context))
expr2 = Multiply(
Add(Variable("x"), Number(1)),
Subtract(Variable("y"), Number(2)),
)
print(expr2.interpret({"x": 5, "y": 8})) # (6)*(6)=36
# 新增运算: 除、幂 → 各加一个类
# 文法扩展容易,但类会变多
应用: 规则引擎
# 场景: 促销规则(满减/折扣)
# "满 100 减 20 且 VIP 双倍积分"
class And(Expression):
def __init__(self, left, right):
self.left, self.right = left, right
def interpret(self, context):
return (bool(self.left.interpret(context))
and bool(self.right.interpret(context)))
class Or(Expression):
def __init__(self, left, right):
self.left, self.right = left, right
def interpret(self, context):
return (bool(self.left.interpret(context))
or bool(self.right.interpret(context)))
class GreaterThan(Expression):
def __init__(self, var, threshold):
self.var, self.threshold = var, threshold
def interpret(self, context):
return context.get(self.var, 0) > self.threshold
class IsVIP(Expression):
def interpret(self, context):
return bool(context.get("vip", False))
# 规则: (金额 > 100) AND (是VIP)
rule = And(GreaterThan("amount", 100), IsVIP())
print(rule.interpret({"amount": 150, "vip": True}))
# True
print(rule.interpret({"amount": 150, "vip": False}))
# False
# 生产建议: 简单规则用解释器,
# 复杂 DSL 用 lark/pyparsing/rulegraph 库
# 或用 Python 表达式 eval(注意安全)
Python 替代方案
# 1. 简单表达式: eval/exec(注意安全!)
# 只对可信输入使用
expr = "x + 5 * 2"
result = eval(expr, {"__builtins__": {}}, {"x": 10})
# 安全: ast.literal_eval(仅字面量)
import ast
ast.literal_eval("[1, 2, 3]") # 安全
# 2. 解析器库(正式 DSL)
# pip install lark
from lark import Lark, Transformer
grammar = """
start: expr
expr: expr "+" expr -> add
| expr "*" expr -> mul
| NUMBER -> num
%import common.NUMBER
%ignore " "
"""
parser = Lark(grammar)
# 3. 规则库: rule-engine / simple-rules
# 4. 配置文件 + dict 映射(最简单)
优缺点总结
# 优点:
# ✅ 文法可扩展(加类)
# ✅ 表达式树清晰
# ✅ 符合开闭原则
# 缺点:
# ❌ 类数量膨胀
# ❌ 复杂文法维护难
# ❌ 性能(树遍历)
# 决策:
# 简单规则/表达式 → 解释器
# 复杂文法 → 解析器库(lark)
# 简单配置 → 直接逻辑判断
# 最佳实践:
# - 文法简单有限时用
# - 提供上下文对象
# - 配合工厂解析文本→表达式树
# - 复杂场景优先选库
解释器让"规则"可编程求值。掌握表达式树与上下文,简单 DSL 与规则引擎手到擒来。
迭代器模式
迭代器模式(Iterator Pattern)
迭代器:提供统一方式顺序访问集合元素,而不暴露集合内部结构。Python 的迭代协议是该模式的语言级实现。
意图
# 问题: 不同集合遍历方式不同
# - 数组/链表/树遍历方式各异
# - 客户端需要知道内部结构
# 解决: 统一迭代器接口(next/has_next)
# 客户端用相同方式遍历所有集合
# 优点:
# - 统一遍历接口
# - 隐藏集合内部
# - 支持多种遍历(正/反/跳跃)
# 缺点:
# - 简单集合过度设计
# - 迭代器失效(修改集合时)
Python 迭代协议
# Python 内置迭代协议:
# __iter__: 返回迭代器
# __next__: 返回下一个(无则 StopIteration)
# 所有内置容器都支持
for x in [1, 2, 3]: ... # 列表
for x in "abc": ... # 字符串
for k in {"a": 1}: ... # 字典
for x in range(5): ... # range
# 手动迭代
it = iter([1, 2, 3])
next(it) # 1
next(it) # 2
next(it) # 3
# next(it) → StopIteration
# 生成器(最简迭代器)
def gen():
yield 1
yield 2
for x in gen(): ...
自定义迭代器
class MyCollection:
def __init__(self, items):
self._items = items
def __iter__(self):
return MyIterator(self)
class MyIterator:
def __init__(self, collection):
self._items = collection._items
self._index = 0
def __iter__(self):
return self
def __next__(self):
if self._index >= len(self._items):
raise StopIteration
value = self._items[self._index]
self._index += 1
return value
col = MyCollection([10, 20, 30])
for x in col:
print(x) # 10 20 30
多种迭代方式
# 集合提供多个迭代器
class BookShelf:
def __init__(self, books):
self.books = books
# 正向迭代
def __iter__(self):
return iter(self.books)
# 反向迭代
def reversed_iter(self):
return reversed(self.books)
# 奇数位迭代(自定义)
def odd_iter(self):
return (b for i, b in enumerate(self.books)
if i % 2 == 0)
shelf = BookShelf(["A", "B", "C", "D"])
print(list(shelf)) # ['A','B','C','D']
print(list(shelf.reversed_iter())) # ['D','C','B','A']
print(list(shelf.odd_iter())) # ['A','C']
生成器迭代(惰性)
# 生成器 = 惰性迭代器(省内存)
def read_lines(filename):
"""逐行读取(大数据友好)"""
with open(filename, encoding="utf-8") as f:
for line in f:
yield line.strip()
# 无限序列
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# 只取前 10 个
import itertools
first10 = list(itertools.islice(fibonacci(), 10))
print(first10) # [0,1,1,2,3,5,8,13,21,34]
# 迭代工具(itertools)
itertools.chain(a, b) # 串联
itertools.cycle(items) # 循环
itertools.repeat(x, 3) # 重复
itertools.zip_longest(a,b) # 长短不一
itertools.groupby(items) # 分组
实际应用
# 1. 自定义容器类(让客户端 for 遍历)
# 2. 惰性大数据(流式读取)
# 3. 树/图的遍历封装(DFS/BFS 迭代器)
# 4. 分页迭代器(自动翻页)
# 分页迭代器示例
class PaginatedIterator:
def __init__(self, fetch_page, page_size=20):
self.fetch = fetch_page
self.page_size = page_size
self.page = 0
self.items = []
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.items):
self.page += 1
self.items = self.fetch(self.page)
self.index = 0
if not self.items:
raise StopIteration
item = self.items[self.index]
self.index += 1
return item
# 使用: 自动翻页加载
def api_page(page):
# 模拟 API 分页
data = [f"第{page}页-{i}" for i in range(3)]
return data if page <= 2 else []
for item in PaginatedIterator(api_page):
print(item)
# 第1页-0,1,2; 第2页-0,1,2
优缺点总结
# 优点:
# ✅ 统一遍历接口
# ✅ 隐藏内部结构
# ✅ 惰性求值(省内存)
# ✅ 多种遍历方式
# 缺点:
# ❌ 简单集合用不上
# ❌ 迭代中修改集合易出错
# Python 优势:
# 迭代协议内建,几乎零成本
# 生成器让惰性迭代极简
# itertools 提供丰富工具
# 最佳实践:
# - 自定义容器实现 __iter__
# - 大数据用生成器
# - 遍历中不修改集合(或操作副本)
# - 多迭代方式用独立方法返回新迭代器
迭代器是遍历的"统一语言",Python 内建支持。掌握 __iter__/__next__ 与生成器,集合遍历灵活高效。
中介者模式
中介者模式(Mediator Pattern)
中介者:用一个中介对象封装一组对象的交互,减少对象间的直接耦合(类似"交通指挥中心")。
意图
# 问题: 多个对象互相交互(网状耦合)
# - 每个对象要知道其他所有对象
# - 新增对象要改所有关联对象
# 解决: 对象只与中介者通信(星形)
# 中介者协调各对象间的交互
# 优点:
# - 减少对象间耦合(网状→星形)
# - 交互逻辑集中
# - 新增对象更简单
# 缺点:
# - 中介者可能膨胀(上帝对象)
# - 交互逻辑集中后难以复用
结构
# 参与者:
# Mediator: 中介者接口(notify)
# ConcreteMediator: 具体中介者(协调)
# Colleague: 同事接口(持中介者引用)
# ConcreteColleague: 具体同事
# 无中介: A→B, A→C, B→C(网状)
# 有中介: A→M, B→M, C→M(星形)
Python 示例: 聊天室
# 中介者: 聊天室
class ChatRoom:
def __init__(self):
self.users = {}
def register(self, user):
self.users[user.name] = user
def send(self, message, sender):
for name, user in self.users.items():
if name != sender.name:
user.receive(f"[{sender.name}] {message}")
def send_to(self, message, sender, target):
if target in self.users:
self.users[target].receive(
f"[{sender.name}->你] {message}")
# 同事: 用户(只与聊天室通信)
class User:
def __init__(self, name, room: ChatRoom):
self.name = name
self.room = room
room.register(self)
def send(self, message):
self.room.send(message, self)
def send_to(self, message, target):
self.room.send_to(message, self, target)
def receive(self, message):
print(f"{self.name} 收到: {message}")
# 使用: 对象之间不直接引用!
room = ChatRoom()
alice = User("Alice", room)
bob = User("Bob", room)
carol = User("Carol", room)
alice.send("大家好!")
# Bob 收到: [Alice] 大家好!
# Carol 收到: [Alice] 大家好!
bob.send_to("你好 Alice", "Alice")
# Alice 收到: [Bob->你] 你好 Alice
# 新增用户: 只需 register,无需改其他类
dave = User("Dave", room)
应用: 表单联动
# 场景: 表单组件联动
# 国家 → 城市 → 邮编; 开关 → 禁用
class FormMediator:
def __init__(self):
self.components = {}
def register(self, name, component):
component.mediator = self
self.components[name] = component
def notify(self, sender, event, data=None):
if sender == "country" and event == "change":
# 联动: 更新城市选项
self.components["city"].set_options(
cities_by_country[data])
self.components["zip"].clear()
elif sender == "toggle" and event == "change":
# 联动: 开关控制输入禁用
self.components["input"].set_disabled(
not data)
# 组件基类
class Component:
def __init__(self, name):
self.name = name
self.mediator = None
def changed(self, event, data=None):
if self.mediator:
self.mediator.notify(
self.name, event, data)
# 使用
mediator = FormMediator()
country = Component("country")
city = Component("city")
toggle = Component("toggle")
input_box = Component("input")
for c in [country, city, toggle, input_box]:
mediator.register(c.name, c)
# 触发联动
country.changed("change", "CN")
toggle.changed("change", False)
# 对象解耦: 组件互相不认识!
与观察者的对比
# 观察者(Observer):
# 一对多通知(发布-订阅)
# 发布者不知道订阅者细节
# 中介者(Mediator):
# 多对多交互(集中协调)
# 对象间不直接通信
# 对比:
# 观察者: 单向通知流
# 中介者: 双向协调交互
# 选择:
# 一对多通知 → 观察者
# 复杂网状交互 → 中介者
# 事件总线常结合两者
# Python 中事件总线/信号槽:
# - blinker / pyee(事件库)
# - Django signals
# 都是中介者+观察者思想的实现
优缺点总结
# 优点:
# ✅ 解耦(网状→星形)
# ✅ 交互逻辑集中
# ✅ 新增对象影响小
# 缺点:
# ❌ 中介者膨胀(上帝对象)
# ❌ 交互逻辑难复用
# ❌ 集中后复杂度高
# 最佳实践:
# - 中介者保持职责聚焦
# - 交互协议清晰(事件/通知)
# - 大系统拆多个中介者
# - 配合观察者模式
# 决策:
# 3-4 个对象交互 → 直接引用即可
# 对象多/交互复杂 → 中介者
# 聊天室/表单联动/UI 协作 → 中介者
中介者把"网状耦合"变为"星形协调"。掌握注册/通知机制,聊天室、表单联动、UI 协作清晰解耦。
备忘录模式
备忘录模式(Memento Pattern)
备忘录:在不破坏封装的前提下,捕获并保存对象的内部状态,以便之后恢复到该状态(快照/存档)。
意图
# 问题: 需要保存/恢复对象状态
# - 撤销/重做
# - 存档/读档(游戏)
# - 直接保存内部字段破坏封装
# 解决: 备忘录对象保存状态快照
# 由"发起人"创建与恢复,外部不可直接访问
# 优点:
# - 不破坏封装
# - 简化发起人(状态管理分离)
# - 支持多级恢复
# 缺点:
# - 备忘录多时内存开销
# - 历史管理复杂
结构
# 参与者:
# Originator: 发起人(创建/恢复备忘录)
# Memento: 备忘录(保存状态,不透明)
# Caretaker: 管理者(保存备忘录列表)
# 关键: 备忘录内部状态对外不可见
# Python 约定用私有/下划线,靠约定
Python 示例: 文本编辑器
# 备忘录(状态快照)
class Memento:
def __init__(self, state):
# 保存不可变副本
self._state = state.copy()
def _get_state(self):
return self._state.copy()
# 发起人
class Editor:
def __init__(self):
self.content = ""
self.cursor = 0
def type(self, text):
self.content += text
self.cursor += len(text)
def save(self) -> Memento:
# 创建快照(封装状态)
return Memento({
"content": self.content,
"cursor": self.cursor,
})
def restore(self, memento: Memento):
# 从快照恢复
state = memento._get_state()
self.content = state["content"]
self.cursor = state["cursor"]
def __str__(self):
return f"Editor('{self.content}', cursor={self.cursor})"
# 管理者: 历史栈
class History:
def __init__(self):
self._stack = []
def push(self, memento):
self._stack.append(memento)
def pop(self):
return self._stack.pop() if self._stack else None
# 使用
editor = Editor()
history = History()
# 输入并保存快照
editor.type("Hello")
history.push(editor.save())
print(editor) # Editor('Hello', cursor=5)
editor.type(" World")
history.push(editor.save())
print(editor) # Editor('Hello World', cursor=11)
editor.type("!")
print(editor) # Editor('Hello World!', cursor=12)
# 撤销
editor.restore(history.pop())
print(editor) # Editor('Hello World', cursor=11)
editor.restore(history.pop())
print(editor) # Editor('Hello', cursor=5)
# 多级撤销 + 恢复
# 需要重做时: 双栈(undo/redo)
应用: 游戏存档
class GameState:
"""备忘录: 游戏状态快照"""
def __init__(self, health, level,
inventory):
self.health = health
self.level = level
self.inventory = inventory.copy()
class Game:
def __init__(self):
self.health = 100
self.level = 1
self.inventory = []
def damage(self, amount):
self.health -= amount
def level_up(self):
self.level += 1
def add_item(self, item):
self.inventory.append(item)
# 创建快照
def create_snapshot(self) -> GameState:
return GameState(self.health, self.level,
self.inventory)
# 恢复快照
def restore(self, state: GameState):
self.health = state.health
self.level = state.level
self.inventory = state.inventory.copy()
# 存档管理(每关自动存档)
game = Game()
saves = []
game.level_up()
game.add_item("剑")
saves.append(game.create_snapshot()) # 存档点1
game.damage(50)
game.add_item("药水")
saves.append(game.create_snapshot()) # 存档点2
# 挂了,读档
game.restore(saves[-1]) # 回到存档点2
print(game.health, game.level, game.inventory)
Python 简化
# 1. 用 copy 快照
import copy
class Editor:
def save(self):
return copy.deepcopy(self.__dict__)
def restore(self, state):
self.__dict__ = copy.deepcopy(state)
# 2. dataclasses.replace(不可变风格)
from dataclasses import dataclass, replace
@dataclass(frozen=True)
class State:
content: str = ""
cursor: int = 0
# 修改 = 生成新状态(不可变)
state = State("Hello", 5)
new_state = replace(state, content="Hi")
# 旧状态自动保留(天然快照)
# 3. 序列化存档(跨会话)
import json
def save_game(game):
return json.dumps(game.__dict__)
def load_game(data):
game = Game()
game.__dict__ = json.loads(data)
return game
优缺点总结
# 优点:
# ✅ 不破坏封装
# ✅ 状态管理分离
# ✅ 多级撤销/存档
# 缺点:
# ❌ 快照内存开销
# ❌ 历史管理复杂
# ❌ 大对象快照成本高
# 最佳实践:
# - 快照保存不可变副本
# - 历史栈限制长度
# - 大对象只保存差异(增量)
# - Python 用 copy/dataclass 简化
# 决策:
# 需要撤销/存档 → 备忘录
# 简单状态 → 直接变量/序列化
# 不可变风格 → dataclass + replace
备忘录是"时间旅行"的快照机制。掌握创建/恢复与历史管理,撤销、存档、状态回滚游刃有余。
观察者模式
观察者模式(Observer Pattern)
观察者:定义一对多依赖,当对象状态变化时,所有依赖者自动收到通知(发布-订阅)。
意图
# 问题: 对象状态变化需要通知多个对象
# - 直接引用: 耦合,新增观察者要改主题
# - 变化频繁: 忘记通知某对象
# 解决: 观察者注册到主题
# 主题变化时批量通知所有观察者
# 优点:
# - 解耦(主题不知道观察者细节)
# - 开闭原则(新增观察者容易)
# - 广播通知
# 缺点:
# - 通知顺序不确定
# - 观察者异常影响主题
# - 内存泄漏(未注销)
结构
# 参与者:
# Subject: 主题(维护观察者列表,通知)
# Observer: 观察者接口(update)
# ConcreteSubject: 具体主题
# ConcreteObserver: 具体观察者
# 例: 天气站(Subject) → 各显示屏(Observer)
Python 示例: 天气站
from abc import ABC, abstractmethod
# 观察者接口
class Observer(ABC):
@abstractmethod
def update(self, temperature, humidity,
pressure): ...
# 主题
class WeatherStation:
def __init__(self):
self._observers = []
self._temperature = 0
self._humidity = 0
# 注册/注销
def attach(self, observer):
self._observers.append(observer)
def detach(self, observer):
self._observers.remove(observer)
# 设置新数据并通知
def set_weather(self, temp, humidity):
self._temperature = temp
self._humidity = humidity
self._notify()
def _notify(self):
for obs in self._observers:
obs.update(self._temperature,
self._humidity, 1013)
# 具体观察者
class PhoneDisplay(Observer):
def __init__(self, name):
self.name = name
def update(self, temp, hum, press):
print(f"{self.name}: {temp}°C, "
f"湿度{hum}%")
class WebDisplay(Observer):
def update(self, temp, hum, press):
print(f"网站显示: {temp}°C")
# 使用
station = WeatherStation()
phone = PhoneDisplay("手机")
web = WebDisplay()
station.attach(phone)
station.attach(web)
station.set_weather(25, 60)
# 手机: 25°C, 湿度60%
# 网站显示: 25°C
# 动态加/减观察者
station.detach(web)
station.set_weather(28, 55)
# 手机: 28°C, 湿度55%(网站不再收到)
事件总线(通用观察者)
# 事件总线: 按事件类型注册观察者
class EventBus:
def __init__(self):
self._listeners = {} # 事件 → [回调]
def on(self, event, callback):
self._listeners.setdefault(
event, []).append(callback)
return callback
def off(self, event, callback):
if event in self._listeners:
self._listeners[event].remove(callback)
def emit(self, event, *args, **kwargs):
for cb in self._listeners.get(event, []):
cb(*args, **kwargs)
# 使用
bus = EventBus()
@bus.on("order_created")
def send_notification(order):
print(f"通知: 新订单 {order['id']}")
@bus.on("order_created")
def deduct_stock(order):
print(f"扣库存: {order['item']}")
bus.emit("order_created",
{"id": 1, "item": "手机"})
# 通知: 新订单 1
# 扣库存: 手机
# 应用: 解耦业务模块
# 下单 → 通知/库存/积分/日志 各听各的
Python 内置信号/回调
# 1. Django signals(Web 项目常用)
from django.dispatch import Signal
order_created = Signal()
def on_order(sender, **kwargs):
print("订单事件:", kwargs)
order_created.connect(on_order)
order_created.send(sender=None, order_id=1)
# 2. 属性监听(描述符)
class Observable:
def __init__(self):
self._value = None
self._callbacks = []
def bind(self, fn):
self._callbacks.append(fn)
@property
def value(self):
return self._value
@value.setter
def value(self, new):
self._value = new
for fn in self._callbacks:
fn(new)
obs = Observable()
obs.bind(lambda v: print(f"变化: {v}"))
obs.value = 42 # 变化: 42
实际应用
# 1. UI 事件(按钮点击/输入变化)
# 2. 数据变化通知(视图更新)
# 3. 消息队列/事件驱动
# 4. 订阅推送(股票/天气/新闻)
# 5. 数据库触发器/监听
# 框架中的观察者:
# - RxJS Observable(响应式)
# - Vue/React 响应式数据
# - Django signals
# - 消息中间件(Redis pub/sub)
# 与中介者对比:
# 观察者: 一对多广播(单向)
# 中介者: 多对多协调(双向集中)
优缺点总结
# 优点:
# ✅ 解耦主题与观察者
# ✅ 广播通知
# ✅ 开闭原则
# 缺点:
# ❌ 通知顺序不确定
# ❌ 观察者异常需隔离
# ❌ 忘记注销(内存泄漏)
# 最佳实践:
# - 观察者回调捕获异常
# - 使用完 detach/off
# - 用弱引用(weakref)防泄漏
# - 复杂场景用事件总线/消息队列
# 决策:
# 一对多通知 → 观察者
# 系统级解耦 → 事件总线/消息队列
# 响应式链式 → RxJS
观察者让"状态变化"自动广播。掌握注册/通知机制与事件总线,通知、联动、事件驱动架构信手拈来。
状态模式
状态模式(State Pattern)
状态:对象的行为随内部状态变化而变化,把每个状态的行为封装为独立的类,消除大量 if-else。
意图
# 问题: 对象行为依赖状态(if-else 泛滥)
# - 状态多且行为各异
# - 状态转换逻辑分散
# 解决: 每个状态一个类
# 对象持有"当前状态",行为委托给状态对象
# 状态转换 = 替换状态对象
# 优点:
# - 消除状态 if-else
# - 状态行为内聚
# - 新增状态容易
# - 转换逻辑集中
# 缺点:
# - 类数量增加
# - 状态多时系统复杂
结构
# 参与者:
# Context: 上下文(持当前状态,委托行为)
# State: 状态接口
# ConcreteState: 具体状态(行为+转换)
# 例: 订单状态
# 待支付 → 已支付 → 已发货 → 已完成
# 每个状态可执行的行为不同
Python 示例: 订单状态机
from abc import ABC, abstractmethod
# 上下文(订单)
class Order:
def __init__(self):
# 初始状态: 待支付
self.state = PendingState()
def set_state(self, state):
print(f"状态: {self} → {state}")
self.state = state
# 委托给当前状态
def pay(self):
self.state.pay(self)
def ship(self):
self.state.ship(self)
def complete(self):
self.state.complete(self)
def __str__(self):
return self.state.__str__()
# 状态接口
class OrderState(ABC):
@abstractmethod
def pay(self, order): ...
@abstractmethod
def ship(self, order): ...
@abstractmethod
def complete(self, order): ...
# 待支付
class PendingState(OrderState):
def pay(self, order):
print("支付成功")
order.set_state(PaidState())
def ship(self, order):
print("错误: 未支付不能发货")
def complete(self, order):
print("错误: 未支付不能完成")
def __str__(self):
return "待支付"
# 已支付
class PaidState(OrderState):
def pay(self, order):
print("错误: 已支付,不能重复支付")
def ship(self, order):
print("发货成功")
order.set_state(ShippedState())
def complete(self, order):
print("错误: 未发货不能完成")
def __str__(self):
return "已支付"
# 已发货
class ShippedState(OrderState):
def pay(self, order):
print("错误: 已发货,无需支付")
def ship(self, order):
print("错误: 已发货,不能重复发货")
def complete(self, order):
print("订单完成")
order.set_state(CompletedState())
def __str__(self):
return "已发货"
# 已完成
class CompletedState(OrderState):
def pay(self, order):
print("错误: 订单已完成")
def ship(self, order):
print("错误: 订单已完成")
def complete(self, order):
print("错误: 已完成,不能重复")
def __str__(self):
return "已完成"
# 使用: 状态驱动的行为
order = Order()
print(order) # 待支付
order.ship() # 错误: 未支付不能发货
order.pay() # 支付成功 → 已支付
order.pay() # 错误: 已支付,不能重复支付
order.ship() # 发货成功 → 已发货
order.complete() # 订单完成 → 已完成
order.complete() # 错误: 已完成,不能重复
# 非法操作被状态对象拦截,逻辑内聚!
状态机(更通用的实现)
# 用 dict 定义状态转换表(轻量状态机)
TRANSITIONS = {
"pending": {
"pay": ("paid", "支付成功"),
"ship": (None, "错误: 未支付不能发货"),
},
"paid": {
"ship": ("shipped", "发货成功"),
"pay": (None, "错误: 已支付"),
},
"shipped": {
"complete": ("completed", "订单完成"),
},
}
class SimpleOrder:
def __init__(self):
self.state = "pending"
def act(self, action):
rule = TRANSITIONS[self.state].get(action)
if not rule:
print(f"错误: 状态[{self.state}] "
f"无操作[{action}]")
return
next_state, message = rule
if next_state:
self.state = next_state
print(message)
o = SimpleOrder()
o.act("pay") # 支付成功
o.act("ship") # 发货成功
o.act("pay") # 错误: 已支付
o.act("complete") # 订单完成
# 或使用第三方库: transitions
# pip install transitions
# 适合复杂状态机(嵌套/动作/守卫)
实际应用
# 1. 订单/审批流程状态机
# 2. 播放器状态(播放/暂停/停止)
# 3. 网络连接状态(连接/断开/重试)
# 4. 游戏角色状态(站立/跑动/跳跃)
# 5. 业务流程引擎
# 状态模式 vs 状态机:
# 状态模式: 面向对象(状态类)
# 状态机: 数据驱动(dict/库)
# 简单状态机用 dict,复杂用库/状态类
# 与策略模式对比:
# 策略: 行为可替换(算法)
# 状态: 行为随状态变(自动切换)
# 状态 = 策略 + 自动状态转换
优缺点总结
# 优点:
# ✅ 消除状态 if-else
# ✅ 状态行为内聚
# ✅ 新增状态容易
# ✅ 非法操作拦截
# 缺点:
# ❌ 类数量增加
# ❌ 状态类间共享上下文需注意
# ❌ 状态转换逻辑分散在状态类
# 最佳实践:
# - 上下文只做委托
# - 状态转换集中在各状态类
# - 非法操作明确报错
# - 简单状态机用 dict/库
# - 状态类尽量无状态(用上下文数据)
状态模式把"状态逻辑"结构化。掌握状态类与委托,订单流、播放器、连接管理等场景告别 if-else 泥潭。
空对象模式
空对象模式(Null Object Pattern)
空对象:用"什么都不做"的对象替代 None,消除客户端大量的 None 判断(null check)。
意图
# 问题: 返回值可能为 None
# - 客户端到处都是 if x is not None
# - 忘记判断 → AttributeError
# 解决: 定义空对象(实现相同接口,但空操作)
# 客户端统一调用,无需判空
# 优点:
# - 消除判空代码
# - 统一客户端逻辑
# - 默认行为优雅(空操作)
# 缺点:
# - 空对象可能隐藏错误
# - 接口需支持空实现
# - 调试时不易发现"为什么没效果"
结构
# 参与者:
# AbstractObject: 抽象接口
# RealObject: 真实对象
# NullObject: 空对象(空操作实现)
# Client: 统一调用,不判空
# 例: 用户查询
# get_user(id) → 真实用户 或 NullUser
Python 示例: 用户系统
from abc import ABC, abstractmethod
# 抽象接口
class User(ABC):
@abstractmethod
def get_name(self) -> str: ...
@abstractmethod
def get_role(self) -> str: ...
@abstractmethod
def is_active(self) -> bool: ...
# 真实用户
class RealUser(User):
def __init__(self, name, role):
self.name = name
self.role = role
def get_name(self): return self.name
def get_role(self): return self.role
def is_active(self): return True
# 空对象
class NullUser(User):
def get_name(self): return "游客"
def get_role(self): return "guest"
def is_active(self): return False
# 工厂: 返回真实或空对象(绝不返回 None)
class UserService:
def __init__(self):
self._users = {"1": RealUser("张三", "admin")}
def get_user(self, user_id) -> User:
# 找不到时返回空对象,而非 None!
return self._users.get(user_id, NullUser())
# 使用: 无需判空!
service = UserService()
for uid in ["1", "999"]:
user = service.get_user(uid)
# 直接调用,不用 if user is None
print(f"{user.get_name()} "
f"({user.get_role()}, "
f"active={user.is_active()})")
# 张三 (admin, active=True)
# 游客 (guest, active=False)
# 对比: 传统 None 方式
user = service.get_user("999")
if user is not None: # 到处判空
print(user.get_name())
else:
print("用户不存在")
# 空对象方式更统一
应用: 日志/命令/配置
# 1. 空日志器
class Logger(ABC):
@abstractmethod
def log(self, msg): ...
class RealLogger(Logger):
def log(self, msg):
print(f"[LOG] {msg}")
class NullLogger(Logger):
def log(self, msg):
pass # 什么都不做
# 配置可关闭日志
logger = RealLogger() if debug else NullLogger()
logger.log("启动") # 统一调用
# 2. 空命令(菜单项无操作)
class Command(ABC):
@abstractmethod
def execute(self): ...
class NoopCommand(Command):
def execute(self):
pass # 空操作
# 3. 默认配置
class Config(ABC):
@abstractmethod
def get(self, key): ...
class NullConfig(Config):
def get(self, key):
return None # 或默认值
# 4. 空集合(替代 None 列表)
EMPTY_LIST = []
def get_items():
return found_items or EMPTY_LIST
# 客户端直接遍历,无需判空
Python 简化
# 1. 默认参数/可调用
def get_user(user_id):
return self._users.get(
user_id, None)
# 客户端用 getattr 默认
user = get_user("999")
name = getattr(user, "name", "游客")
role = getattr(user, "role", "guest")
# 2. 返回默认对象
DEFAULT_USER = RealUser("游客", "guest")
def get_user(user_id):
return self._users.get(user_id,
DEFAULT_USER)
# 注意: 共享默认对象需不可变
# 3. dict 默认值
self._users = defaultdict(lambda: RealUser(
"游客", "guest"))
# 4. 可选属性用 None-safe 工具
# 或 pydantic 默认值
from dataclasses import dataclass, field
@dataclass
class UserDC:
name: str = "游客"
role: str = "guest"
active: bool = False
优缺点总结
# 优点:
# ✅ 消除判空
# ✅ 客户端逻辑统一
# ✅ 默认行为清晰
# 缺点:
# ❌ 空对象可能掩盖错误
# ❌ 接口需支持空实现
# ❌ 调试困难(无日志无报错)
# 最佳实践:
# - 空对象保持"语义明确"(游客/默认)
# - 关键路径慎用(可能隐藏 bug)
# - 空对象记录可观测性(可选)
# - 配合工厂返回(客户端无感)
# 决策:
# 频繁判空且默认行为明确 → 空对象
# 需要区分"不存在"场景 → 保持 None
# 错误需要暴露 → 用异常/None
空对象用"空操作"替代判空。掌握空对象设计与默认行为,客户端代码干净统一,少一堆 if 判断。
策略模式
策略模式(Strategy Pattern)
策略:定义一系列算法并封装,让它们可以互相替换。客户端在运行时选择策略,消除大量条件分支。
意图
# 问题: 同一操作有多种算法实现
# - if-else 选择算法,难扩展
# - 算法代码混杂在业务中
# 解决: 每个算法一个策略类
# 客户端持策略引用,运行时切换
# 优点:
# - 消除算法选择 if-else
# - 算法独立扩展(开闭原则)
# - 运行时动态切换
# - 算法可复用/可测试
# 缺点:
# - 类数量增加
# - 客户端需了解策略差异
结构
# 参与者:
# Strategy: 策略接口
# ConcreteStrategy: 具体策略(算法)
# Context: 上下文(持策略,委托执行)
# Client: 选择策略注入 Context
# 例: 排序算法
# Context(排序器) + Bubble/Quick/Merge 策略
Python 示例: 支付方式
from abc import ABC, abstractmethod
# 策略接口
class PaymentStrategy(ABC):
@abstractmethod
def pay(self, amount) -> str: ...
# 具体策略
class AlipayStrategy(PaymentStrategy):
def pay(self, amount):
return f"支付宝支付 {amount} 元"
class WechatStrategy(PaymentStrategy):
def pay(self, amount):
return f"微信支付 {amount} 元"
class BankCardStrategy(PaymentStrategy):
def pay(self, amount):
return f"银行卡支付 {amount} 元"
# 上下文(持策略)
class ShoppingCart:
def __init__(self, strategy=None):
self.items = []
self.strategy = strategy
def add_item(self, name, price):
self.items.append((name, price))
def set_strategy(self, strategy):
self.strategy = strategy
def checkout(self):
total = sum(price for _, price in self.items)
if not self.strategy:
raise ValueError("请选择支付方式")
return self.strategy.pay(total)
# 使用: 运行时切换策略
cart = ShoppingCart()
cart.add_item("手机", 3999)
cart.add_item("耳机", 599)
# 选择支付方式
cart.set_strategy(AlipayStrategy())
print(cart.checkout()) # 支付宝支付 4598 元
# 换策略(无需改购物车代码!)
cart.set_strategy(WechatStrategy())
print(cart.checkout()) # 微信支付 4598 元
cart.set_strategy(BankCardStrategy())
print(cart.checkout()) # 银行卡支付 4598 元
# 新增支付方式: 加一个策略类即可
class CryptoStrategy(PaymentStrategy):
def pay(self, amount):
return f"加密货币支付 {amount} 元"
cart.set_strategy(CryptoStrategy())
print(cart.checkout())
Python 简化: 函数策略
# 策略 = 函数(简单场景)
from typing import Callable
def alipay(amount): return f"支付宝 {amount}"
def wechat(amount): return f"微信 {amount}"
def bankcard(amount): return f"银行卡 {amount}"
class Cart:
def __init__(self, pay_func: Callable = None):
self.pay_func = pay_func
self.total = 0
def checkout(self):
return self.pay_func(self.total)
cart = Cart(alipay)
cart.total = 100
print(cart.checkout()) # 支付宝 100
cart.pay_func = wechat # 换策略
print(cart.checkout()) # 微信 100
# 或字典选择
PAYMENTS = {
"alipay": alipay,
"wechat": wechat,
}
pay_func = PAYMENTS.get("alipay")
pay_func(100)
应用场景
# 1. 支付/配送/优惠策略
# 2. 排序/搜索算法选择
# 3. 压缩/加密算法
# 4. 校验规则(不同场景不同规则)
# 5. 价格计算(会员/促销)
# 框架应用:
# - 排序 key 函数
# sorted(items, key=len)
# - 校验器(Validator 模式)
# - Django 存储后端/缓存后端
# 与状态模式对比:
# 策略: 算法替换(客户端主动)
# 状态: 行为随状态(自动切换)
# 策略无状态转换,状态有
# 与模板方法对比:
# 策略: 整体算法替换
# 模板: 固定骨架,子类填步骤
优缺点总结
# 优点:
# ✅ 消除算法 if-else
# ✅ 开闭原则(加策略即可)
# ✅ 运行时切换
# ✅ 策略独立测试
# 缺点:
# ❌ 类数量增加
# ❌ 客户端需了解策略
# ❌ 策略间有隐性联系时注意
# 最佳实践:
# - 策略接口最小化
# - 简单策略用函数/dict
# - 策略工厂统一创建
# - 无状态策略可共享实例
# 决策:
# 算法多变/可选 → 策略
# 简单分支 → dict 映射
# 参数化行为 → 函数参数
策略模式让"算法"可插拔。掌握策略注入与切换,支付、排序、校验类场景灵活替换。
模板模式
模板模式(Template Method Pattern)
模板:在父类中定义算法的骨架(步骤顺序),将部分步骤延迟到子类实现。复用骨架,定制细节。
意图
# 问题: 多个流程结构相同,细节不同
# - 复制粘贴 → 修改一处忘改他处
# - 流程顺序必须一致
# 解决: 父类模板方法定义步骤骨架
# 子类只实现差异步骤(钩子)
# 优点:
# - 复用骨架(代码复用)
# - 保证流程一致性
# - 控制扩展点(钩子方法)
# 缺点:
# - 父类与子类耦合
# - 骨架修改影响所有子类
结构
# 参与者:
# AbstractClass: 抽象类(模板方法 + 抽象步骤)
# ConcreteClass: 具体子类(实现差异步骤)
# 模板方法: 固定调用顺序
# def template_method(self):
# step1()
# step2() # 子类实现
# step3()
# 钩子方法: 可选覆盖(默认空/默认行为)
Python 示例: 数据导入流程
from abc import ABC, abstractmethod
# 抽象类: 定义流程骨架
class DataImporter(ABC):
# 模板方法(骨架,不可覆盖)
def import_data(self, source):
data = self.fetch(source) # 1
cleaned = self.clean(data) # 2
self.validate(cleaned) # 3
result = self.save(cleaned) # 4
self.notify(result) # 5
return result
# 公共步骤(可直接实现)
def fetch(self, source):
print(f"读取: {source}")
return [{"raw": source, "n": 1}]
def notify(self, result):
print(f"完成: {len(result)} 条")
# 差异步骤(子类实现)
@abstractmethod
def clean(self, data): ...
@abstractmethod
def validate(self, data): ...
@abstractmethod
def save(self, data): ...
# 具体子类: CSV 导入
class CsvImporter(DataImporter):
def clean(self, data):
print("清洗 CSV 数据")
return [{"n": d["n"] * 1.0} for d in data]
def validate(self, data):
print("校验 CSV: 无空值")
assert all(d["n"] >= 0 for d in data)
def save(self, data):
print("保存到 MySQL")
return data
# 具体子类: API 导入
class ApiImporter(DataImporter):
def fetch(self, source):
print(f"API 请求: {source}")
return [{"n": i} for i in range(3)]
def clean(self, data):
print("清洗 API 数据")
return [d for d in data if d["n"] != 0]
def validate(self, data):
print("校验 API: 限 5 条")
assert len(data) <= 5
def save(self, data):
print("写入 MongoDB")
return data
# 使用: 流程骨架一致,细节各异
CsvImporter().import_data("data.csv")
# 读取: data.csv
# 清洗 CSV 数据
# 校验 CSV: 无空值
# 保存到 MySQL
# 完成: 1 条
ApiImporter().import_data("https://api.x.com")
# API 请求: https://api.x.com
# 清洗 API 数据
# 校验 API: 限 5 条
# 写入 MongoDB
# 完成: 3 条
# 骨架固定: 顺序不可乱(父类控制)
# 差异: 每步实现不同(子类定制)
钩子方法
# 钩子: 可选覆盖(默认行为)
class ReportGenerator(ABC):
def generate(self):
header = self.get_header()
body = self.get_body()
footer = self.get_footer()
return f"{header}\n{body}\n{footer}"
# 公共默认实现(钩子)
def get_header(self):
return "=== 报告 ==="
def get_footer(self):
return "=== 结束 ==="
@abstractmethod
def get_body(self): ...
# 是否包含页脚(钩子,可覆盖)
def include_footer(self):
return True
class SalesReport(ReportGenerator):
def get_body(self):
return "本月销售额: 100万"
def get_footer(self):
# 覆盖页脚
return f"{super().get_footer()} | 内部机密"
class SummaryReport(ReportGenerator):
def get_body(self):
return "汇总: 正常"
def include_footer(self):
return False # 不要页脚
print(SalesReport().generate())
print(SummaryReport().generate())
实际应用
# 1. 数据导入/导出流程
# 2. 报表生成(头/体/尾)
# 3. 框架生命周期(init/start/stop)
# 4. 测试用例(SetUp/Test/TearDown)
# 5. 构建流程(编译/测试/打包)
# 框架中的模板方法:
# - pytest 夹具(setup/teardown)
# - Django Form 校验(clean 系列)
# - 算法骨架(排序的对比器)
# 与策略模式对比:
# 模板: 骨架固定,子类填步骤(继承)
# 策略: 算法整体替换(组合)
# 模板适合"流程固定细节多变"
优缺点总结
# 优点:
# ✅ 复用流程骨架
# ✅ 保证流程一致
# ✅ 扩展点清晰(抽象/钩子)
# 缺点:
# ❌ 继承耦合
# ❌ 骨架修改影响全局
# ❌ 子类太多时层级深
# 最佳实践:
# - 模板方法控制步骤(不要子类覆盖)
# - 抽象步骤明确(abstractmethod)
# - 钩子给默认行为(可覆盖)
# - 骨架稳定后再抽象
# Python 注意:
# 模板方法不要被装饰器/子类意外覆盖
# 用 final(3.12)或约定保护骨架
模板方法固定"骨架"复用,子类定制"细节"。掌握抽象步骤与钩子,导入、报表、生命周期类流程统一有序。
访问者模式
访问者模式(Visitor Pattern)
访问者:在不修改元素类的前提下,为对象结构增加新操作。将"操作"与"数据结构"分离。
意图
# 问题: 对结构中的对象执行多种操作
# - 每加一个操作要改所有元素类
# - 操作逻辑分散在各元素中
# 解决: 操作封装为访问者
# 元素接受访问者,调用对应 visit 方法
# 优点:
# - 新增操作容易(加访问者)
# - 操作逻辑集中
# - 元素类稳定(不修改)
# 缺点:
# - 新增元素类型要改所有访问者
# - 访问者需了解元素内部
# - 双向分派实现复杂
# 适用: 结构稳定、操作多变
结构
# 参与者:
# Visitor: 访问者接口(visit(元素))
# ConcreteVisitor: 具体访问者(操作)
# Element: 元素接口(accept(访问者))
# ConcreteElement: 具体元素
# ObjectStructure: 对象结构(遍历)
# 例: 文件系统
# 文件/目录(结构稳定) + 大小/压缩/备份(操作多变)
Python 示例: 报表系统
from abc import ABC, abstractmethod
# 元素接口
class Employee(ABC):
@abstractmethod
def accept(self, visitor): ...
# 具体元素
class Developer(Employee):
def __init__(self, name, salary, lines):
self.name = name
self.salary = salary
self.code_lines = lines
def accept(self, visitor):
return visitor.visit_developer(self)
class Manager(Employee):
def __init__(self, name, salary, reports):
self.name = name
self.salary = salary
self.report_count = reports
def accept(self, visitor):
return visitor.visit_manager(self)
# 访问者接口
class Visitor(ABC):
@abstractmethod
def visit_developer(self, emp): ...
@abstractmethod
def visit_manager(self, emp): ...
# 具体访问者: 薪资报告
class SalaryReport(Visitor):
def visit_developer(self, emp):
return (f"开发 {emp.name}: "
f"{emp.salary}元/月")
def visit_manager(self, emp):
return (f"经理 {emp.name}: "
f"{emp.salary}元/月")
# 具体访问者: 绩效评分
class PerformanceVisitor(Visitor):
def visit_developer(self, emp):
score = min(emp.code_lines // 500, 10)
return f"开发 {emp.name}: {score}分"
def visit_manager(self, emp):
score = min(emp.report_count * 2, 10)
return f"经理 {emp.name}: {score}分"
# 使用: 新增操作 = 新增访问者,不改员工类
employees = [
Developer("张三", 20000, 3000),
Manager("李四", 30000, 4),
Developer("王五", 18000, 1200),
]
for emp in employees:
print(emp.accept(SalaryReport()))
print(emp.accept(PerformanceVisitor()))
# 新增"培训需求"访问者...
class TrainingVisitor(Visitor):
def visit_developer(self, emp):
return f"{emp.name}: 需要 AI 课程"
def visit_manager(self, emp):
return f"{emp.name}: 需要管理课"
# 元素类零修改,操作无限扩展!
Python 简化: 单分派/模式匹配
# Python 可用模式匹配简化(3.10+)
from dataclasses import dataclass
@dataclass
class Dev:
name: str
salary: float
@dataclass
class Mgr:
name: str
salary: float
# 操作函数(无访问者类)
def salary_report(emp):
match emp:
case Dev(name=n, salary=s):
return f"开发 {n}: {s}"
case Mgr(name=n, salary=s):
return f"经理 {n}: {s}"
case _:
return "未知"
# 或单分派
from functools import singledispatch
@singledispatch
def report(emp):
return f"未知 {emp}"
@report.register
def _(emp: Dev):
return f"开发 {emp.name}: {emp.salary}"
@report.register
def _(emp: Mgr):
return f"经理 {emp.name}: {emp.salary}"
print(report(Dev("张三", 20000)))
print(report(Mgr("李四", 30000)))
# 新增类型: 注册新函数,元素类零修改
# 简单场景用 singledispatch 更 Pythonic
实际应用
# 1. 编译器: AST 分析(语义检查/优化)
# 2. 报表: 对不同实体生成不同报表
# 3. 序列化: 对不同对象序列化
# 4. 统计: 遍历结构做各类统计
# 5. 文档导出: HTML/PDF/JSON 导出器
# 条件:
# ✅ 对象结构稳定(少变)
# ✅ 操作频繁增加
# ❌ 结构常变 → 不要用访问者
# ❌ 操作少 → 普通方法即可
# 注意:
# 访问者模式在 Python 中较少必要
# singledispatch / match 更简洁
# 大型类型层次时访问者有优势
优缺点总结
# 优点:
# ✅ 新增操作容易
# ✅ 操作逻辑集中
# ✅ 元素类稳定
# 缺点:
# ❌ 新增元素类型需改所有访问者
# ❌ 破坏封装(访问者需了解内部)
# ❌ 双向分派复杂
# Python 决策:
# 简单层次 → singledispatch
# 结构稳定+操作多变 → 访问者
# 复杂类型系统 → 考虑访问者或 match
# 反模式警示:
# 元素类型频繁变化时,访问者
# 会导致大规模修改,避免使用
访问者把"操作"从"结构"中分离。掌握 accept/visit 双向分派,结构稳定而操作多变的场景游刃有余。
MVC 模式
MVC 模式(Model-View-Controller)
MVC 是经典的架构模式:将应用分为模型(Model)、视图(View)、控制器(Controller)三层,实现关注点分离。
意图
# 问题: 业务逻辑、数据、界面混杂
# - 代码难以维护
# - 修改界面影响业务
# - 难以测试
# 解决: 三层分离
# Model: 数据与业务逻辑
# View: 界面展示
# Controller: 处理输入,协调 Model 与 View
# 优点:
# - 关注点分离
# - 可独立修改与测试
# - 代码复用(多 View 共享 Model)
# 缺点:
# - 学习曲线
# - 简单应用过度分层
结构
# 请求流:
# 用户 → Controller(处理输入)
# ↓ 更新
# Model(数据/业务)
# ↓ 通知
# View(展示)
# 依赖方向:
# Controller → Model + View
# View → Model(只读)
# Model 不依赖 View/Controller
Python 示例: 任务管理
# Model: 数据与业务
class TaskModel:
def __init__(self):
self.tasks = []
self._observers = []
def add(self, title):
self.tasks.append(
{"title": title, "done": False})
self.notify()
def toggle(self, index):
self.tasks[index]["done"] = not self.tasks[index]["done"]
self.notify()
def get_tasks(self):
return self.tasks
def attach(self, observer):
self._observers.append(observer)
def notify(self):
for obs in self._observers:
obs.update(self.tasks)
# View: 展示
class TaskView:
def update(self, tasks):
print("\n=== 任务列表 ===")
for i, t in enumerate(tasks):
status = "✓" if t["done"] else "□"
print(f"{i}. [{status}] {t['title']}")
# Controller: 输入处理
class TaskController:
def __init__(self, model, view):
self.model = model
self.view = view
model.attach(view) # 数据变化 → 视图更新
def add_task(self, title):
self.model.add(title)
def complete_task(self, index):
self.model.toggle(index)
# 使用
model = TaskModel()
view = TaskView()
controller = TaskController(model, view)
controller.add_task("学习 Python")
controller.add_task("写报告")
# === 任务列表 ===
# 0. [□] 学习 Python
# 1. [□] 写报告
controller.complete_task(0)
# === 任务列表 ===
# 0. [✓] 学习 Python
# 1. [□] 写报告
# 数据与展示分离:
# - 换 View(网页/终端/APP)不影响 Model
# - Model 可独立单元测试
# - Controller 可单独测输入逻辑
框架中的 MVC
# Django: MVT(视图=Controller)
# Model → ORM 模型
# View(函数/类) → 控制器逻辑
# Template → 视图层
# Flask/FastAPI: MVC 风格
# 路由函数 = Controller
# SQLAlchemy 模型 = Model
# 模板/JSON 响应 = View
# 前端框架:
# Angular/React/Vue = MVVM(变体)
# ViewModel 绑定视图与模型
# 对比:
# MVC: Controller 处理输入
# MVVM: ViewModel 自动绑定(数据驱动)
# 命令/事件驱动:
# 简单 MVC 可以不用观察者
# 用 Controller 手动刷新 View 也行
MVC 变体
# MVP(Model-View-Presenter):
# Presenter 完全控制 View(接口)
# View 被动,适合单元测试
# Android 常用
# MVVM(Model-View-ViewModel):
# ViewModel 双向绑定
# View 声明式(绑定语法)
# WPF/Angular/Vue
# 选择:
# 简单应用 → 分层即可(MVC 简化)
# 需要强测试 → MVP
# 数据绑定框架 → MVVM
# 架构层次:
# MVC 是"模式",微服务是"架构"
# MVC 可在微服务内部使用
优缺点总结
# 优点:
# ✅ 关注点分离
# ✅ 可测试性
# ✅ 复用与并行开发
# ✅ 维护性
# 缺点:
# ❌ 学习成本
# ❌ 简单应用过度设计
# ❌ Controller 可能臃肿
# 最佳实践:
# - Model 纯业务(不依赖 UI)
# - Controller 薄(只编排)
# - View 只展示(无业务)
# - 数据流单向(清晰可追踪)
# - 简单 CRUD 用框架默认分层
# Python 建议:
# - 小工具: 函数式分层即可
# - Web: 用框架自带结构(不硬套)
# - 桌面: MVVM 更适合
MVC 是软件架构的"地基"。理解三层职责与数据流,无论 Web/桌面/移动,项目结构清晰可维护。
业务代表模式
业务代表模式(Business Delegate Pattern)
业务代表:客户端与业务服务之间的中间层,隐藏服务查找与访问细节,减少客户端与业务层耦合。
意图
# 问题: 客户端直接访问业务服务
# - 需要知道服务查找逻辑(JNDI/注册)
# - 服务地址/接口变化影响客户端
# 解决: 业务代表封装查找与调用
# 客户端只与代表交互
# 优点:
# - 解耦客户端与服务
# - 隐藏服务查找
# - 统一访问点(缓存/降级)
# 缺点:
# - 增加一层
# - 简单应用过度设计
结构
# 参与者:
# Client: 客户端(调代表)
# BusinessDelegate: 业务代表(封装)
# BusinessService: 业务服务接口
# ServiceLookup: 服务查找(定位实现)
# ConcreteService: 具体服务
# 例: 客户端下单 → 代表 → 查找并调用订单服务
Python 示例: 业务服务访问
from abc import ABC, abstractmethod
# 业务服务接口
class BusinessService(ABC):
@abstractmethod
def do_processing(self) -> str: ...
# 具体服务
class OrderService(BusinessService):
def do_processing(self):
return "订单服务处理中"
class PaymentService(BusinessService):
def do_processing(self):
return "支付服务处理中"
# 服务查找
class ServiceLookup:
def __init__(self):
self._services = {
"order": OrderService(),
"payment": PaymentService(),
}
def get_service(self, name) -> BusinessService:
service = self._services.get(name)
if not service:
raise ValueError(f"未知服务: {name}")
return service
# 业务代表
class BusinessDelegate:
def __init__(self):
self.lookup = ServiceLookup()
self.service_type = "order"
def set_service(self, service_type):
self.service_type = service_type
def do_task(self) -> str:
service = self.lookup.get_service(
self.service_type)
# 可加缓存/日志/重试
return service.do_processing()
# 客户端: 只认识代表
class Client:
def __init__(self, delegate):
self.delegate = delegate
def run(self):
return self.delegate.do_task()
delegate = BusinessDelegate()
client = Client(delegate)
delegate.set_service("order")
print(client.run()) # 订单服务处理中
delegate.set_service("payment")
print(client.run()) # 支付服务处理中
# 客户端不知道服务如何查找/实例化
# 换服务实现不影响客户端
# 实际应用:
# - RPC/微服务客户端封装
# - 业务网关(统一入口)
# - 服务代理(缓存/降级/限流)
Python 简化
# 1. 函数式代表
SERVICES = {
"order": lambda: "订单处理",
"payment": lambda: "支付处理",
}
def business_delegate(name):
# 封装查找与调用
service = SERVICES.get(name)
if not service:
raise ValueError(f"未知: {name}")
return service()
print(business_delegate("order"))
# 2. 依赖注入(现代替代)
# 客户端直接注入服务(无需代表)
class OrderService:
def process(self): return "订单"
class App:
def __init__(self, order_service):
self.orders = order_service
app = App(OrderService())
app.orders.process()
# 3. 服务定位器(见 J2EE 模式章节)
# 与代表区别: 代表封装"使用",定位器封装"查找"
优缺点总结
# 优点:
# ✅ 解耦客户端与服务
# ✅ 隐藏查找细节
# ✅ 统一访问点(可加横切逻辑)
# ✅ 服务变更影响小
# 缺点:
# ❌ 增加一层
# ❌ 简单应用过度设计
# 最佳实践:
# - 代表保持薄(只转发)
# - 横切逻辑(日志/重试/缓存)放代表
# - 现代项目用依赖注入/服务层
# - 微服务场景用 API 网关
# 决策:
# 客户端多且服务查找复杂 → 业务代表
# 现代 Python → 依赖注入
# 分布式 → 网关/服务发现
业务代表是"客户端与服务的中间人"。掌握查找封装与统一访问,服务调用层解耦灵活。
组合实体模式
组合实体模式(Composite Entity Pattern)
组合实体:用粗粒度实体(聚合对象)表示一组细粒度对象,简化持久化与客户端交互。
意图
# 问题: 业务对象由多个相关对象组成
# - 客户端需分别操作多个对象
# - 持久化时多次读写
# 解决: 组合实体聚合多个"依赖对象"
# 客户端操作组合实体,统一读写
# 优点:
# - 简化客户端(一个对象)
# - 统一事务边界
# - 依赖对象自动管理
# 缺点:
# - 粒度粗,更新可能过度
# - 组合对象大时内存开销
结构
# 参与者:
# CompositeEntity: 组合实体(聚合依赖)
# CoarseGrainedObject: 粗粒度对象(生命周期)
# DependentObject: 依赖对象(细粒度)
# Client: 客户端(操作组合实体)
# 例: 用户资料
# UserEntity(组合) → Profile + Address
Python 示例: 用户资料
# 依赖对象(细粒度)
class ProfileData:
def __init__(self):
self.nickname = ""
self.email = ""
def set_data(self, nickname, email):
self.nickname = nickname
self.email = email
def get_data(self):
return (self.nickname, self.email)
class AddressData:
def __init__(self):
self.city = ""
self.street = ""
def set_data(self, city, street):
self.city = city
self.street = street
def get_data(self):
return (self.city, self.street)
# 粗粒度对象: 管理依赖对象生命周期
class UserCoarseGrained:
def __init__(self):
self.profile = ProfileData()
self.address = AddressData()
def set_data(self, nickname, email,
city, street):
self.profile.set_data(nickname, email)
self.address.set_data(city, street)
def get_data(self):
return {
"nickname": self.profile.nickname,
"email": self.profile.email,
"city": self.address.city,
"street": self.address.street,
}
# 组合实体: 对外统一接口
class UserCompositeEntity:
def __init__(self):
self.coarse = UserCoarseGrained()
def set_data(self, nickname, email,
city, street):
# 统一设置(内部协调多个依赖)
self.coarse.set_data(
nickname, email, city, street)
def get_data(self):
# 统一读取
return self.coarse.get_data()
# 模拟持久化
def save(self):
data = self.get_data()
print(f"保存用户: {data}")
# 客户端: 只与组合实体交互
class Client:
def __init__(self):
self.entity = UserCompositeEntity()
def update_user(self, info):
self.entity.set_data(
info["nickname"], info["email"],
info["city"], info["street"])
self.entity.save()
client = Client()
client.update_user({
"nickname": "小王",
"email": "w@e.com",
"city": "北京",
"street": "长安街1号",
})
# 保存用户: {'nickname': '小王', ...}
# 客户端无需关心 Profile/Address 内部
# 依赖对象可独立复用,也可被组合
实际应用
# 1. 用户资料聚合(基本信息+地址+偏好)
# 2. 订单聚合(订单+明细+收货信息)
# 3. 文档聚合(内容+样式+元数据)
# 4. 表单聚合(多区块表单统一提交)
# 现代对应: 聚合根(DDD)
# 领域驱动设计中的 Aggregate Root:
# 聚合根统一管理内部实体
# 事务边界 = 聚合边界
# 对比:
# 组合实体: J2EE 经典模式
# 聚合根: DDD 现代演进
# 核心一致: 粗粒度对象管理细粒度
# Python 中常表现为:
# - dataclass 组合
# - Pydantic 嵌套模型
# - ORM 关系(joined 加载)
Python 简化: dataclass/Pydantic
from dataclasses import dataclass, field
@dataclass
class Profile:
nickname: str = ""
email: str = ""
@dataclass
class Address:
city: str = ""
street: str = ""
@dataclass
class User:
# 组合实体(聚合)
profile: Profile = field(default_factory=Profile)
address: Address = field(default_factory=Address)
def to_dict(self):
return {
"nickname": self.profile.nickname,
"email": self.profile.email,
"city": self.address.city,
"street": self.address.street,
}
user = User()
user.profile.nickname = "小王"
user.address.city = "北京"
print(user.to_dict())
# 或 Pydantic(校验+序列化)
# from pydantic import BaseModel
# class UserModel(BaseModel):
# profile: Profile
# address: Address
优缺点总结
# 优点:
# ✅ 简化客户端
# ✅ 统一事务/持久化
# ✅ 依赖对象自动管理
# ✅ 结构清晰
# 缺点:
# ❌ 粒度粗(更新过度)
# ❌ 大对象内存开销
# ❌ 依赖对象复用受限
# 最佳实践:
# - 组合实体边界清晰(内聚)
# - 依赖对象可测试
# - 按业务聚合(非任意组合)
# - 现代用 dataclass/Pydantic/DDD 聚合
# 决策:
# 多对象统一操作 → 组合实体
# 简单数据 → dataclass
# 复杂业务 → DDD 聚合根
组合实体用"粗粒度"简化"细粒度"。掌握聚合与统一接口,用户、订单、文档类复合对象管理简洁。
数据访问对象模式
数据访问对象模式(DAO Pattern)
DAO(Data Access Object):将数据访问逻辑(数据库/API/文件)封装为独立对象,业务层不直接接触数据源细节。
意图
# 问题: 业务代码直接写 SQL/ORM 调用
# - 数据源变化(MySQL→PostgreSQL)改业务
# - 查询逻辑分散,难复用/测试
# 解决: DAO 集中数据访问
# 业务层只依赖 DAO 接口
# 优点:
# - 业务与数据源解耦
# - 数据访问复用与测试
# - 换数据源只改 DAO
# 缺点:
# - 增加抽象层
# - 简单 CRUD 略显繁琐
结构
# 参与者:
# DAO: 数据访问接口
# ConcreteDAO: 具体实现(MySQL/API/内存)
# Model/Entity: 数据对象
# Client(业务层): 使用 DAO
# 例: UserDAO
# 接口: get/find/add/update/delete
# 实现: MySQLDAO / MockDAO / APIDAO
Python 示例: 用户 DAO
from abc import ABC, abstractmethod
from dataclasses import dataclass
# 数据对象
@dataclass
class User:
id: int
name: str
email: str
# DAO 接口
class UserDAO(ABC):
@abstractmethod
def get(self, user_id: int) -> User: ...
@abstractmethod
def find_all(self) -> list[User]: ...
@abstractmethod
def add(self, user: User) -> User: ...
@abstractmethod
def update(self, user: User) -> User: ...
@abstractmethod
def delete(self, user_id: int) -> bool: ...
# 内存实现(测试/演示用)
class InMemoryUserDAO(UserDAO):
def __init__(self):
self._data = {}
self._next_id = 1
def get(self, user_id):
return self._data.get(user_id)
def find_all(self):
return list(self._data.values())
def add(self, user):
user.id = self._next_id
self._next_id += 1
self._data[user.id] = user
return user
def update(self, user):
if user.id not in self._data:
return None
self._data[user.id] = user
return user
def delete(self, user_id):
return self._data.pop(user_id, None) is not None
# 业务层(服务): 只依赖 DAO 接口
class UserService:
def __init__(self, dao: UserDAO):
self.dao = dao
def register(self, name, email):
# 业务逻辑 + DAO 调用
if not email or "@" not in email:
raise ValueError("邮箱无效")
return self.dao.add(User(0, name, email))
def get_profile(self, user_id):
user = self.dao.get(user_id)
if not user:
raise ValueError("用户不存在")
return user
def delete_account(self, user_id):
return self.dao.delete(user_id)
# 使用: 注入任意 DAO 实现
service = UserService(InMemoryUserDAO())
user = service.register("张三", "z@e.com")
print(user.id, user.name) # 1 张三
print(service.get_profile(1).email) # z@e.com
service.delete_account(1)
# 换数据源: 实现 MySQLDAO 注入即可
# 业务层零修改!
MySQL 实现
import pymysql
class MysqlUserDAO(UserDAO):
def __init__(self, conn):
self.conn = conn
def find_all(self):
with self.conn.cursor() as cur:
cur.execute(
"SELECT id, name, email FROM users")
rows = cur.fetchall()
return [User(*row) for row in rows]
def add(self, user):
with self.conn.cursor() as cur:
cur.execute(
"INSERT INTO users(name, email) "
"VALUES(%s, %s)",
(user.name, user.email))
user.id = cur.lastrowid
self.conn.commit()
return user
def delete(self, user_id):
with self.conn.cursor() as cur:
n = cur.execute(
"DELETE FROM users WHERE id=%s",
(user_id,))
self.conn.commit()
return n > 0
# 参数化查询防注入!
# 业务层不变,只换 DAO
# service = UserService(MysqlUserDAO(conn))
Python 生态: ORM 即 DAO
# SQLAlchemy(声明式 DAO)
from sqlalchemy import create_engine, select
from sqlalchemy.orm import Session, declarative_base
from sqlalchemy import Column, Integer, String
Base = declarative_base()
class UserORM(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String(50))
email = Column(String(100))
# 使用(Session 类似 DAO)
engine = create_engine("sqlite:///app.db")
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add(UserORM(name="张三", email="z@e.com"))
session.commit()
users = session.scalars(
select(UserORM)).all()
# 仓储模式(Repository, DDD):
# DAO 聚焦数据访问
# Repository 更聚焦领域聚合
# 现代 Python 用 ORM + 服务层即可
实际应用
# 1. 数据库访问封装(DAO)
# 2. 第三方 API 封装(远程数据)
# 3. 缓存访问封装
# 4. 测试替身(内存/模拟 DAO)
# 分层:
# Controller → Service(业务) → DAO → DB
# 业务层不出现 SQL/ORM 调用
# 好处:
# - 单元测试用 Mock DAO(快)
# - 换库/换 API 影响小
# - 查询优化集中一处
# 反模式:
# 业务逻辑写在 DAO 里(DAO 应纯数据)
# 每表一个 DAO 而无业务抽象
# 过度封装简单 CRUD
优缺点总结
# 优点:
# ✅ 数据源解耦
# ✅ 复用与测试
# ✅ 集中管理(连接/事务/缓存)
# ✅ 参数化查询(安全)
# 缺点:
# ❌ 抽象层成本
# ❌ 简单 CRUD 繁琐
# 最佳实践:
# - DAO 纯数据访问(无业务)
# - 接口抽象(可换实现)
# - 参数化查询防注入
# - 事务边界清晰
# - 现代用 ORM + Repository
# 决策:
# 多数据源/需要测试 → DAO 接口
# 单数据源简单 → ORM 直接用
# 大型项目 → 分层 + 仓储
DAO 让"数据访问"可替换、可测试。掌握接口抽象与实现分离,数据层稳定,业务层清爽。
前端控制器模式
前端控制器模式(Front Controller)
前端控制器:所有请求先经过统一入口(控制器),在入口完成公共处理(认证/日志/路由),再分发到具体处理器。
意图
# 问题: 每个请求处理都要重复公共逻辑
# - 认证/日志/权限散落各处
# - 路由管理分散
# 解决: 统一前端控制器
# 所有请求 → 前端控制器 → 公共处理 → 分发
# 优点:
# - 公共逻辑集中(一处修改全局生效)
# - 统一入口(安全/审计)
# - 路由集中管理
# 缺点:
# - 单点故障/性能瓶颈
# - 控制器可能臃肿
结构
# 参与者:
# FrontController: 前端控制器(入口)
# Dispatcher: 分发器(路由到处理者)
# Handler/View: 具体处理者
# 例: Web 框架路由
# 请求 → 中间件(公共) → 路由 → 视图函数
Python 示例: Web 请求
# 处理器
class HomeHandler:
def get(self):
return "首页"
class UserHandler:
def get(self, user_id):
return f"用户 {user_id}"
class NotFoundHandler:
def get(self):
return "404 页面", 404
# 分发器(路由)
class Dispatcher:
def __init__(self):
self.routes = {
"/": HomeHandler(),
"/user": UserHandler(),
}
def dispatch(self, path):
handler = self.routes.get(path)
if not handler:
return NotFoundHandler().get()
return handler.get()
# 前端控制器(统一入口)
class FrontController:
def __init__(self):
self.dispatcher = Dispatcher()
# 公共处理: 认证
def _auth(self, token):
return token == "valid-token"
# 公共处理: 日志
def _log(self, path):
print(f"[访问] {path}")
def handle(self, path, token):
# 1. 公共逻辑(认证)
if not self._auth(token):
return "401 未授权", 401
# 2. 公共逻辑(日志)
self._log(path)
# 3. 分发
return self.dispatcher.dispatch(path)
# 使用: 客户端只面对一个入口
controller = FrontController()
print(controller.handle("/", "invalid"))
# (401 未授权, 401)
print(controller.handle("/", "valid-token"))
# [访问] /
# 首页
print(controller.handle("/user", "valid-token"))
# [访问] /user
# 用户
# 新增页面: 加路由即可
# 公共逻辑(认证/日志)统一,零重复!
框架中的前端控制器
# Flask: 请求钩子
@app.before_request
def auth():
if not check_token():
return "401", 401
@app.before_request
def logging():
print(f"访问: {request.path}")
# 所有视图自动经过钩子(前端控制器思想)
# FastAPI: 中间件
from starlette.middleware.base import BaseHTTPMiddleware
class AuthMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
if not valid(request.headers.get("token")):
return JSONResponse({"err": "401"}, 401)
return await call_next(request)
app.add_middleware(AuthMiddleware)
# Django: MIDDLEWARE 列表
# 认证/日志/CSRF 都是"前端控制器"的一环
# 对比:
# 前端控制器 = 手动实现中间件
# 现代框架用中间件(更解耦)
手动实现 vs 中间件
# 前端控制器:
# 优点: 集中、直观
# 缺点: 需自己维护分发
# 中间件(现代推荐):
# 优点: 可插拔、顺序控制
# 缺点: 抽象、需理解执行顺序
# 选择:
# 简单应用 → 前端控制器(直观)
# 框架项目 → 中间件(标准)
# 需要灵活组合 → 中间件
# 公共处理清单(集中放):
# - 认证/授权
# - 日志/审计
# - 限流/防抖
# - 请求日志(耗时)
# - 统一错误处理
# - 国际化/头处理
优缺点总结
# 优点:
# ✅ 公共逻辑集中
# ✅ 统一入口(安全)
# ✅ 路由集中管理
# ✅ 易于添加横切功能
# 缺点:
# ❌ 单点瓶颈
# ❌ 控制器臃肿风险
# ❌ 需维护分发逻辑
# 最佳实践:
# - 公共逻辑放控制器/中间件
# - 控制器薄(只编排,不写业务)
# - 按需拆分中间件链
# - 现代框架用中间件机制
# 决策:
# 框架项目 → 中间件
# 自研小型服务 → 前端控制器
# 需要统一入口 → 网关(微服务)
前端控制器是"统一入口"的经典方案。掌握公共处理与分发,认证、日志、路由统一管理,现代框架里就是中间件。
拦截过滤器模式
拦截过滤器模式(Intercepting Filter)
拦截过滤器:在请求处理前后插入一系列过滤器,执行公共横切关注点(认证、日志、压缩、加密),且可按需组合。
意图
# 问题: 请求处理需要附加处理
# - 认证/日志/压缩等横切逻辑
# - 顺序与组合需要灵活配置
# 解决: 过滤器链
# 请求 → 过滤器1 → 过滤器2 → 目标处理
# 与前端控制器区别:
# 前端控制器: 统一入口(集中)
# 拦截过滤器: 可插拔处理链(灵活)
# 优点:
# - 横切关注点分离
# - 可插拔/可组合
# - 顺序可控
# - 复用过滤器
# 缺点:
# - 链管理复杂
# - 顺序错误影响功能
结构
# 参与者:
# Filter: 过滤器接口
# ConcreteFilter: 具体过滤器
# FilterChain: 过滤器链(管理顺序)
# Target: 目标处理器
# FilterManager: 过滤器管理器(组装)
# 例: Web 请求
# 认证 → 日志 → 压缩 → 业务
Python 示例: 请求过滤链
from abc import ABC, abstractmethod
# 过滤器接口
class Filter(ABC):
@abstractmethod
def execute(self, request) -> bool: ...
# 返回 False 中断链
# 具体过滤器
class AuthFilter(Filter):
def execute(self, request):
if not request.get("token"):
print("认证失败,拦截")
return False
print("认证通过")
return True
class LogFilter(Filter):
def execute(self, request):
print(f"日志: {request.get('path')}")
return True
class RateLimitFilter(Filter):
def __init__(self, limit=100):
self.limit = limit
self.count = 0
def execute(self, request):
self.count += 1
if self.count > self.limit:
print("限流拦截")
return False
return True
# 过滤器链
class FilterChain:
def __init__(self):
self.filters = []
def add(self, filter: Filter):
self.filters.append(filter)
return self
def execute(self, request) -> bool:
for f in self.filters:
if not f.execute(request):
return False # 中断
return True
# 目标处理
class Target:
def execute(self, request):
return f"执行业务: {request.get('path')}"
# 管理器
class FilterManager:
def __init__(self, target: Target):
self.chain = FilterChain()
self.target = target
def add_filter(self, f: Filter):
self.chain.add(f)
return self
def process(self, request):
if self.chain.execute(request):
return self.target.execute(request)
return "请求被拦截"
# 使用: 灵活组装过滤器
manager = FilterManager(Target())
manager.add_filter(AuthFilter())
manager.add_filter(RateLimitFilter(limit=2))
manager.add_filter(LogFilter())
print(manager.process({"path": "/api", "token": None}))
# 认证失败,拦截
# 请求被拦截
print(manager.process({"path": "/api", "token": "ok"}))
# 认证通过
# 日志: /api
# 执行业务: /api
# 换组合(去掉限流,换顺序)
manager2 = FilterManager(Target())
manager2.add_filter(LogFilter())
manager2.add_filter(AuthFilter())
print(manager2.process({"path": "/x", "token": "ok"}))
# 日志: /x(先日志)
# 认证通过
# 执行业务: /x
# 过滤器可插拔、可复用、顺序可配!
框架中的过滤器
# Django 中间件(过滤器思想)
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.middleware.common.CommonMiddleware",
]
# Flask 钩子
@app.before_request # 认证/限流
@app.after_request # 压缩/头处理
# FastAPI 中间件
@app.middleware("http")
async def add_header(request, call_next):
response = await call_next(request)
response.headers["X-Custom"] = "1"
return response
# 与责任链模式的关系:
# 拦截过滤器 = 责任链 + 横切过滤
# 责任链: 处理者依次尝试
# 过滤器: 前置/后置处理(不替代目标)
常用过滤器
# 前置过滤器(请求前):
# - 认证/授权
# - 限流/防爬
# - 参数校验
# - 请求日志
# - 解密/解压
# 后置过滤器(响应后):
# - 响应日志/耗时
# - 压缩/加密
# - 头注入(CORS/缓存)
# - 统一错误格式
# 实现要点:
# - 过滤器无状态(可复用)
# - 失败返回明确结果
# - 顺序敏感(认证在前)
# - 可配置(白名单/开关)
优缺点总结
# 优点:
# ✅ 横切逻辑分离
# ✅ 可插拔组合
# ✅ 顺序可控
# ✅ 复用过滤器
# 缺点:
# ❌ 链管理复杂
# ❌ 顺序错误难排查
# ❌ 过滤器间隐式依赖
# 最佳实践:
# - 过滤器单一职责
# - 顺序文档化/配置化
# - 提供跳过/白名单机制
# - 日志记录链的执行
# - 框架中用中间件机制
# 决策:
# 框架项目 → 中间件
# 自定义管道 → 过滤器链
# 复杂规则 → 责任链+过滤器
拦截过滤器让"横切关注点"可插拔。掌握链的组装与顺序,认证、日志、限流如积木般灵活组合。
服务定位器模式
服务定位器模式(Service Locator)
服务定位器:集中注册与查找服务,客户端通过定位器获取服务实例,减少直接依赖(服务注册表)。
意图
# 问题: 客户端直接创建/查找服务
# - 服务依赖关系分散
# - 换实现要改多处
# 解决: 服务定位器注册所有服务
# 客户端向定位器请求服务
# 优点:
# - 集中管理服务(注册/生命周期)
# - 客户端解耦(只需定位器)
# - 懒加载(需要时创建)
# 缺点:
# - 隐藏依赖(服务变全局状态)
# - 难测试(需重置定位器)
# - 与依赖注入相比不透明
# 现代建议:
# 依赖注入(DI)通常更优
# 服务定位器用于特殊情况
结构
# 参与者:
# ServiceLocator: 服务定位器(注册+查找)
# Service: 服务接口
# ConcreteService: 具体服务
# Client: 客户端(通过定位器获取服务)
# 例:
# locator.get("payment") → 支付服务实例
Python 示例
from abc import ABC, abstractmethod
# 服务接口
class NotificationService(ABC):
@abstractmethod
def send(self, msg): ...
class EmailService(NotificationService):
def send(self, msg):
return f"[邮件] {msg}"
class SmsService(NotificationService):
def send(self, msg):
return f"[短信] {msg}"
# 服务定位器
class ServiceLocator:
_services = {}
_initialized = False
@classmethod
def init(cls):
# 注册服务(集中配置)
cls._services = {
"email": EmailService(),
"sms": SmsService(),
}
cls._initialized = True
@classmethod
def get(cls, name):
if not cls._initialized:
cls.init()
service = cls._services.get(name)
if not service:
raise ValueError(f"服务未注册: {name}")
return service
@classmethod
def register(cls, name, service):
cls._services[name] = service
@classmethod
def reset(cls):
cls._services = {}
cls._initialized = False
# 客户端: 通过定位器获取服务
class OrderService:
def notify(self, user, message):
# 从定位器获取服务(客户端不直接创建)
email = ServiceLocator.get("email")
return email.send(f"{user}: {message}")
# 使用
order_svc = OrderService()
print(order_svc.notify("张三", "订单已发货"))
# [邮件] 张三: 订单已发货
# 换实现/加服务: 改定位器注册,客户端零改动
ServiceLocator.register("sms", SmsService())
print(ServiceLocator.get("sms").send("促销"))
# [短信] 促销
# 懒加载版本
class LazyServiceLocator:
_factories = {}
_cache = {}
@classmethod
def register(cls, name, factory):
cls._factories[name] = factory
@classmethod
def get(cls, name):
if name not in cls._cache:
cls._cache[name] = cls._factories[name]()
return cls._cache[name]
LazyServiceLocator.register(
"email", EmailService)
svc = LazyServiceLocator.get("email") # 首次创建
svc2 = LazyServiceLocator.get("email") # 复用
服务定位器 vs 依赖注入
# 服务定位器(SL):
# 客户端主动调用: locator.get("svc")
# 依赖隐藏在客户端代码中
# 测试需 reset 定位器
# 依赖注入(DI):
# 客户端声明: __init__(self, svc)
# 依赖显式可见(构造参数)
# 测试直接注入 mock
# 对比:
# DI: 显式、可测试、推荐
# SL: 隐式、集中、特殊场景
# 何时用 SL(慎用):
# - 遗留系统集成
# - 插件体系(运行时注册)
# - 全局框架服务(日志/配置)
# - 无法使用 DI 的框架
# Python 生态:
# 优先: 构造注入 / 模块级对象
# 库: dependency-injector(现代 DI)
# 简单: 模块注册表
Python 简化
# 1. 模块级注册表
# services.py
_services = {}
def register(name, service):
_services[name] = service
def get(name):
return _services[name]
# 2. 模块级单例(最简单)
# email_service.py
service = EmailService()
# 直接 import 使用(服务定位的极端简化)
# 3. 依赖注入(推荐)
class OrderService:
def __init__(self, notifier):
self.notifier = notifier # 注入
# 测试:
order_svc = OrderService(FakeNotifier())
# 4. dependency-injector(生产级)
# from dependency_injector import containers, providers
# container = Containers()
# container.email_service()
优缺点总结
# 优点:
# ✅ 集中服务管理
# ✅ 客户端解耦
# ✅ 懒加载
# ✅ 运行时注册(插件)
# 缺点:
# ❌ 隐藏依赖(全局状态)
# ❌ 测试需 reset
# ❌ 服务生命周期管理复杂
# 最佳实践:
# - 优先依赖注入
# - SL 用于插件/框架服务
# - 接口抽象(可替换实现)
# - 明确服务生命周期
# - 测试提供 reset/替换
# 决策:
# 标准业务 → DI
# 插件体系 → SL
# 全局基础设施 → SL(日志/配置)
服务定位器集中"注册与查找"。了解它与依赖注入的权衡,插件、全局服务场景用对工具。
传输对象模式
传输对象模式(Transfer Object / DTO)
传输对象(DTO,Data Transfer Object):在进程/网络间传输数据的对象,只含数据不含业务逻辑。用于批量传输与解耦。
意图
# 问题: 跨层/跨网络传输数据
# - 一次传输多个字段(减少调用)
# - 暴露内部实体(安全/耦合)
# 解决: DTO 承载数据
# 实体 ↔ DTO 转换,接口传输 DTO
# 优点:
# - 减少传输次数(批量)
# - 隔离实体(不暴露内部)
# - 按需裁剪字段
# - 序列化友好
# 缺点:
# - 转换样板代码
# - DTO 与实体同步维护
结构
# 参与者:
# TransferObject: 传输对象(数据载体)
# Entity: 业务实体
# Service: 业务服务(转换 DTO)
# Client: 客户端(接收 DTO)
# 例:
# UserEntity(内部) ↔ UserDTO(传输)
# API 返回 UserDTO(不含密码等)
Python 示例
from dataclasses import dataclass
# 业务实体(内部,含敏感字段)
class User:
def __init__(self, id, name, email, password):
self.id = id
self.name = name
self.email = email
self.password = password # 敏感!
# 传输对象(DTO): 只含需传输字段
@dataclass
class UserDTO:
id: int
name: str
email: str
# 不含 password!
def to_dict(self):
return {
"id": self.id,
"name": self.name,
"email": self.email,
}
# 业务服务: 实体 ↔ DTO 转换
class UserService:
def get_user_dto(self, user_id) -> UserDTO:
entity = self._find_entity(user_id)
# 实体 → DTO(隐藏敏感字段)
return UserDTO(
id=entity.id,
name=entity.name,
email=entity.email,
)
def _find_entity(self, user_id):
return User(user_id, "张三",
"z@e.com", "secret123")
# 客户端: 只接触 DTO
svc = UserService()
dto = svc.get_user_dto(1)
print(dto.to_dict())
# {'id': 1, 'name': '张三', 'email': 'z@e.com'}
# 密码不会泄露!
批量传输对象
# 批量 DTO(一次传输多个)
@dataclass
class UsersBatchDTO:
users: list
total: int
page: int
def to_dict(self):
return {
"users": [u.to_dict() for u in self.users],
"total": self.total,
"page": self.page,
}
# 服务返回批量 DTO
def list_users_dto(self, page):
entities = self._find_page(page)
dtos = [UserDTO(e.id, e.name, e.email)
for e in entities]
return UsersBatchDTO(dtos, total=len(dtos), page=page)
# 优点: 一次调用返回所有数据
# 避免 N 次调用(减少网络开销)
现代 Python: Pydantic DTO
# Pydantic 是最流行的 DTO 工具
from pydantic import BaseModel
class UserDTO(BaseModel):
id: int
name: str
email: str
# 自动校验 + 序列化
dto = UserDTO(id=1, name="张三", email="z@e.com")
print(dto.model_dump())
# {'id': 1, 'name': '张三', 'email': 'z@e.com'}
# 从实体转换
user = User(1, "张三", "z@e.com", "secret")
dto = UserDTO.model_validate({
"id": user.id,
"name": user.name,
"email": user.email,
})
# FastAPI 中即接口响应模型
@app.get("/users/{uid}", response_model=UserDTO)
def get_user(uid: int):
entity = find(uid)
return UserDTO(id=entity.id,
name=entity.name,
email=entity.email)
# 响应自动过滤敏感字段
# Pydantic 优势: 校验/转换/文档自动生成
实际应用
# 1. API 请求/响应模型
# 2. 跨服务数据传输
# 3. 批量数据交换(报表/导入)
# 4. 序列化安全(隐藏敏感字段)
# 5. 数据版本控制
# 分层建议:
# Controller ← DTO → Service ← Entity
# 接口层用 DTO,内部用 Entity
# 避免实体直接暴露
# 反模式:
# - DTO 塞满业务逻辑(应纯数据)
# - DTO 与实体完全重复(失去意义)
# - 无必要地复制数据(简单场景直接用 dict)
优缺点总结
# 优点:
# ✅ 减少传输次数
# ✅ 隔离实体(安全)
# ✅ 按需裁剪字段
# ✅ 序列化友好
# 缺点:
# ❌ 转换样板代码
# ❌ 维护成本(同步变更)
# ❌ 数据冗余(复制)
# 最佳实践:
# - DTO 纯数据(不可变 dataclass)
# - 敏感字段不进入 DTO
# - 批量传输减少调用
# - 用 Pydantic/dataclass 简化
# - 明确转换边界
# 决策:
# 跨层/网络传输 → DTO
# 简单内部使用 → dict/dataclass
# API 开发 → Pydantic 模型
传输对象(DTO)是数据交换的"标准信封"。掌握实体-DTO 转换与 Pydantic,接口层安全高效,内部结构不泄露。
设计模式资源
设计模式资源
本节汇总设计模式的学习资源、工具与实践建议,帮助你从"认识模式"走向"用好模式"。
经典书籍
# 必读经典
# 1. 《Design Patterns》(GoF 四人组)
# 设计模式开山之作,23 个模式权威定义
# 2. 《Head First Design Patterns》
# 图解入门首选,Java 示例,易读
# 3. 《Refactoring》(Martin Fowler)
# 重构与模式的结合(识别坏味道→套模式)
# 4. 《Patterns of Enterprise Application Architecture》
# 企业应用模式(分层/数据源/并发)
# Python 专项
# 5. 《Python Design Patterns》(笔者的朋友可参考)
# 6. 《Architecture Patterns with Python》
# 现代 Python 架构(仓储/事件驱动/DDD)
在线资源
# 设计模式参考站
# - refactoring.guru(图解,多语言,强烈推荐)
# - sourcemaking.com(模式+反模式)
# - dofactory.com(速查表)
# Python 示例
# - github 搜索 "python design patterns"
# 常见仓库: faif/python-patterns(经典)
# 视频课程
# - YouTube: Refactoring Guru 频道
# - 国内: B站设计模式教程(Java/Go 为主)
# 学习路径建议:
# 1. refactoring.guru 通读一遍(图解理解)
# 2. 动手写 Python 示例(本教程可参考)
# 3. 在项目里识别与重构应用
模式速查表
# 创建型(5)
# 工厂: 创建对象解耦
# 抽象工厂: 产品族
# 单例: 全局唯一
# 建造者: 分步构建
# 原型: 克隆对象
# 结构型(7)
# 适配器: 接口转换
# 桥接: 抽象与实现分离
# 组合: 树形统一
# 装饰器: 动态加功能
# 外观: 简化接口
# 享元: 共享省内存
# 代理: 控制访问
# 行为型(11)
# 责任链: 请求链式处理
# 命令: 请求封装(撤销)
# 解释器: 文法解释
# 迭代器: 统一遍历
# 中介者: 集中协调
# 备忘录: 状态快照
# 观察者: 发布订阅
# 状态: 状态驱动行为
# 策略: 算法替换
# 模板: 骨架复用
# 访问者: 操作分离
# J2EE(10)
# MVC / 业务代表 / 组合实体 / DAO
# 前端控制器 / 拦截过滤器 / 服务定位器 / DTO
模式选择思维
# 面对设计问题时的思考顺序:
# 1. 能不能不引入模式?(最简单方案优先)
# 2. 变化点是什么?(识别变化维度)
# 3. 选哪个模式?(按变化点匹配)
# 4. 如何最小化引入?(只在需要处用)
# 变化点 → 模式:
# 对象创建变化 → 工厂/原型
# 行为算法变化 → 策略/模板
# 状态行为变化 → 状态
# 通知需求 → 观察者
# 接口不匹配 → 适配器
# 功能动态添加 → 装饰器
# 请求处理链 → 责任链
# 撤销需求 → 命令/备忘录
# 记住: 模式解决"变化"与"重复"
# 没有变化点,就没有模式的价值
实践建议
# 1. 从"识别"开始
# 读代码时标注用到的模式(很多框架都是)
# 2. 先重构后套模式
# 有坏味道(重复/巨大类/条件分支)
# 才考虑模式(Refactoring 配合)
# 3. Python 特色思考
# 部分模式在 Python 有更简写法:
# 单例→模块, 迭代器→生成器
# 策略→函数, 命令→闭包
# 空对象→默认值, 观察者→事件库
# 4. 团队沟通
# 用模式名交流(设计评审更高效)
# 5. 面试准备
# 理解意图与场景,能画结构图
# 会写小示例,能说优缺点
# 6. 持续积累
# 每个项目复盘: 用了什么模式,效果如何
最后的话
# 设计模式是"经验",不是"规则"
# 核心原则(比模式更重要):
# - 简单优先(KISS)
# - 单一职责(SRP)
# - 开闭原则(OCP)
# - 组合优于继承
# 用模式的正确姿势:
# 先有"问题",再找"模式"
# 而不是"会用模式",到处硬套
# 随着经验增长:
# 你会从"记模式"到"自然设计"
# 模式融入直觉,成为内功
# 本教程是起点,实践是最好的老师。
# 多写、多读、多思考,祝你成为
# 一名优秀的软件设计师!