本文是 Python 元编程与动态特性 的姊妹篇,聚焦面向对象设计的「套路」层——如何组织类与对象,让代码可扩展、可测试、可维护。
目录
1. 设计模式在 Python 中的位置
设计模式是解决特定场景问题的经过验证的模板。Python 因动态特性、一等函数和鸭子类型,很多经典模式「方言」比 Go 书里的 C++/Java 版本更简洁。
| 语言特性 | 替代了哪些模式 |
|---|---|
| 装饰器 | 装饰器模式、代理模式 |
| 描述符 | 观察者(属性级)、策略 |
| 生成器/迭代器 | 迭代器模式、状态机 |
| 上下文管理器 | 资源管理模板 |
| 鸭子类型 | 策略模式、接口隔离 |
| 一等函数 | 策略、命令、观察者 |
核心观点:模式是「思维工具」,不是「教条」。Python 里很多模式可以用更 Pythonic 的方式表达,甚至根本不需要类。
2. SOLID 五原则
| 原则 | 含义 | Python 体现 |
|---|---|---|
| S 单一职责 | 一个类只做一件事 | 拆分 class / 模块 |
| O 开闭 | 对扩展开放、对修改关闭 | @abstractmethod + 策略注入 |
| L 里氏替换 | 子类可替换父类 | 鸭子类型天然 |
| I 接口隔离 | 不强迫依赖不需要的接口 | Protocol(PEP 544) |
| D 依赖倒置 | 依赖抽象不依赖具体 | 类型注解 + 依赖注入 |
from typing import Protocol
# ISP:用 Protocol 定义最小接口
class Notifier(Protocol):
def send(self, message: str) -> None: ...
# DIP:高层模块依赖抽象 Notifier,不依赖具体 EmailSms
class OrderService:
def __init__(self, notifier: Notifier):
self._notifier = notifier # 依赖注入
class EmailNotifier:
def send(self, message: str) -> None:
print(f"[email] {message}")
class SmsNotifier:
def send(self, message: str) -> None:
print(f"[sms] {message}")
service = OrderService(EmailNotifier())
service2 = OrderService(SmsNotifier()) # 开闭:新增通知方式不改 OrderService
3. 创建型模式
3.1 单例(Singleton)
Python 的单例有四种写法,模块级是 Pythonic 首选:
# 方式一:模块级对象(最 Pythonic,且天然线程安全)
# db.py
_connection = None
def get_connection():
global _connection
if _connection is None:
_connection = connect_to_db()
return _connection
# 方式二:__new__ 控制
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
# 方式三:装饰器
def singleton(cls):
instances = {}
def get(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get
@singleton
class Config: ...
3.2 工厂(Factory)与建造者(Builder)
# 简单工厂:根据类型创建对象
class Animal:
def speak(self): raise NotImplementedError
class Dog(Animal):
def speak(self): return "汪汪"
class Cat(Animal):
def speak(self): return "喵喵"
class AnimalFactory:
_registry = {"dog": Dog, "cat": Cat}
@classmethod
def create(cls, kind: str) -> Animal:
try:
return cls._registry[kind]()
except KeyError:
raise ValueError(f"未知动物: {kind}")
# 建造者:链式设置可选参数
class Pizza:
def __init__(self):
self.size = None; self.toppings = []
def __repr__(self): return f"Pizza(size={self.size}, toppings={self.toppings})"
class PizzaBuilder:
def __init__(self): self._pizza = Pizza()
def size(self, s): self._pizza.size = s; return self
def add_topping(self, t): self._pizza.toppings.append(t); return self
def build(self): return self._pizza
pizza = PizzaBuilder().size("L").add_topping("cheese").add_topping("mushroom").build()
4. 结构型模式
4.1 适配器(Adapter)
# 让不兼容接口协同工作
class OldPayment:
def pay(self, amount): return f"Old pay {amount}"
class NewPayment:
def charge(self, cents): return f"New charge {cents}"
class NewPaymentAdapter:
"""把 NewPayment 适配成旧接口"""
def __init__(self, new: NewPayment): self._new = new
def pay(self, amount): return self._new.charge(int(amount * 100))
def checkout(payment):
print(payment.pay(99.9))
checkout(OldPayment())
checkout(NewPaymentAdapter(NewPayment()))
4.2 装饰器(Decorator)与代理(Proxy)
装饰器模式在 Python 里就是语言特性 @decorator:
import functools, time
def retry(max_attempts=3, delay=1.0):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise
print(f"重试 {attempt+1}: {e}")
time.sleep(delay)
return wrapper
return decorator
@retry(max_attempts=3)
def fetch_data():
...
# 代理:延迟加载 + 权限控制
class LazyFile:
def __init__(self, path): self.path = path; self._content = None
@property
def content(self):
if self._content is None:
self._content = open(self.path, encoding="utf-8").read()
return self._content
5. 行为型模式
5.1 观察者(Observer)
Python 里事件通知是观察者的经典场景:
class EventEmitter:
def __init__(self):
self._listeners = {}
def on(self, event, fn):
self._listeners.setdefault(event, []).append(fn)
return self
def emit(self, event, *args):
for fn in self._listeners.get(event, []):
fn(*args)
# 使用
emitter = EventEmitter()
emitter.on("login", lambda user: print(f"通知用户 {user} 登录"))
emitter.on("login", log_security_event) # 多个订阅者
emitter.emit("login", "alice")
5.2 策略(Strategy)与模板方法(Template Method)
# 策略:注入可替换算法(用函数即可,不必类)
def bubble_sort(data): return sorted(data)
def quick_sort(data): return sorted(data) # 示意
class Sorter:
def __init__(self, strategy=None):
self._strategy = strategy or bubble_sort
def sort(self, data):
return self._strategy(data)
# 模板方法:父类定骨架,子类填实现
class DataPipeline:
def run(self):
data = self.load() # 子类实现
cleaned = self.clean(data) # 子类实现
self.save(cleaned) # 子类实现
self.report(len(cleaned)) # 骨架(默认实现)
class CsvPipeline(DataPipeline):
def load(self): ...
def clean(self, data): ...
def save(self, cleaned): ...
6. Pythonic 替代:用语言特性简化经典模式
| 经典模式 | Python 替代 | 代码量对比 |
|---|---|---|
| Iterator | 生成器 yield | 1/5 |
| State | functools.partial / 枚举 | 1/3 |
| Command | 函数/functools.partial | 1/4 |
| Visitor | functools.singledispatch | 1/5 |
| Memento | copy.deepcopy | 1/6 |
| Factory Method | dict 映射 + 函数 | 1/3 |
from functools import singledispatch
# Visitor → singledispatch(多态分派)
@singledispatch
def area(shape):
raise NotImplementedError(f"不支持 {type(shape)}")
class Circle: ...
class Rect: ...
@area.register
def _(shape: Circle):
return 3.14 * shape.radius ** 2
@area.register
def _(shape: Rect):
return shape.w * shape.h
# 生成器替代迭代器模式
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
7. 函数式模式
Python 支持函数式编程风格,配合类型注解形成「数据管道」架构:
from typing import Iterable, Callable, TypeVar
T = TypeVar("T")
# 管道模式:数据经一串纯函数变换
def pipe(data: T, *fns: Callable[[T], T]) -> T:
for fn in fns:
data = fn(data)
return data
# 组合
def compose(*fns):
def _apply(x):
for fn in reversed(fns):
x = fn(x)
return x
return _apply
# 使用
result = pipe(
read_csv("data.csv"),
filter_rows(lambda r: r["age"] > 18),
sort_by("salary", reverse=True),
take(10),
)
函数式模式的价值:纯函数无副作用 → 可测试、可并行、可缓存。适合数据处理、业务规则链。
8. 模式选型与过度设计
8.1 什么时候用模式
| 信号 | 建议模式 |
|---|---|
| 多个相似算法可切换 | 策略 |
| 对象创建逻辑分支多 | 工厂 |
| 事件发生需通知多方 | 观察者 |
| 新增类型需处理不同逻辑 | singledispatch |
| 资源初始化昂贵且共享 | 单例(模块级) |
8.2 过度设计的警示
错误示范:
├── AbstractPaymentProcessorFactory(抽象工厂)
├── IPaymentValidator(接口)
├── PaymentValidatorImpl(实现)
├── PaymentContext(上下文)
└── 40 个类只为「扣一笔钱」
正确做法:
class Payment:
def __init__(self, amount): ...
def validate(self): ...
def process(self): ...
判断标准:现在只有一种实现?→ 不抽象。两种以上?→ 用函数参数。将来可能变?→ 用 Protocol 定义接口,但别建类层级。
9. 实战:一个可维护的支付系统
综合运用 SOLID + 模式,实现一个易扩展的支付系统:
from typing import Protocol
from dataclasses import dataclass
# --- 领域层(DIP:依赖抽象)---
class PaymentGateway(Protocol):
def charge(self, amount_cents: int) -> str: ...
@dataclass
class Order:
amount: int
currency: str = "CNY"
# --- 具体实现(可插拔)---
class StripeGateway:
def charge(self, amount_cents: int) -> str:
return f"Stripe charged {amount_cents}"
class AlipayGateway:
def charge(self, amount_cents: int) -> str:
return f"Alipay charged {amount_cents}"
# --- 工厂:按配置选择实现 ---
GATEWAYS = {
"stripe": StripeGateway,
"alipay": AlipayGateway,
}
class GatewayFactory:
@classmethod
def create(cls, name: str) -> PaymentGateway:
try:
return GATEWAYS[name]()
except KeyError:
raise ValueError(f"不支持的支付渠道: {name}")
# --- 服务:依赖注入 + 开闭 ---
class PaymentService:
def __init__(self, gateway: PaymentGateway):
self._gateway = gateway # 构造注入
def pay(self, order: Order) -> str:
return self._gateway.charge(order.amount * 100)
# --- 装配 ---
service = PaymentService(GatewayFactory.create("alipay"))
print(service.pay(Order(99.9))) # Alipay charged 9990
扩展性:新增支付渠道只需加一个类 + 注册到 GATEWAYS,PaymentService 零改动(开闭原则)。
10. 速查表与最佳实践
| 场景 | 模式/特性 | Python 实现要点 |
|---|---|---|
| 全局唯一对象 | 单例 | 模块级对象 |
| 按类型创建对象 | 工厂 | dict 注册表 |
| 对象初始化复杂 | 建造者 | 链式方法 |
| 接口不兼容 | 适配器 | 薄封装 |
| 动态加功能 | 装饰器 | @decorator |
| 事件通知 | 观察者 | EventEmitter |
| 算法可替换 | 策略 | 函数参数 |
| 算法骨架固定 | 模板方法 | 父类 + 子类 |
| 类型多态处理 | Visitor | singledispatch |
| 资源共享 | 对象池 | @contextmanager |
最佳实践:
- 优先用语言特性(函数、装饰器、生成器、Protocol)替代经典模式。
- 一个模块一个职责,类要小。
- 用类型注解 + Protocol 表达「依赖抽象」。
- 没有两种以上实现前,不要抽象。
- 模式是为可读性服务的,读不懂的模式就是坏模式。
一句话记忆:Python 设计模式 = SOLID 定边界 + 经典模式当工具箱 + 语言特性做简化剂——能用
yield就不写迭代器类,能用函数参数就不建策略类。
延伸阅读
- Python 元编程与动态特性 —— 描述符、元类与代码生成
- Python 类型系统与 Pydantic V2 —— Protocol 与接口设计
- Python 装饰器与上下文管理器 —— 装饰器模式实战
- [[java-enterprise]] —— Java 设计模式对比
- [[golang]] —— Go 的组合与接口哲学
模式是经验的沉淀,不是约束的枷锁。在 Python 里,最好的「设计模式」往往是那些让代码最短、最直白、最好测的写法。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。