Python 设计模式与 SOLID:从经典模式到 Pythonic 简化

Python 设计模式完整指南:SOLID 五原则、创建型/结构型/行为型经典模式、用 Python 语言特性(装饰器/描述符/生成器/鸭子类型)简化经典模式、函数式模式、模式选型与过度设计、实战架构示例。

本文是 Python 元编程与动态特性 的姊妹篇,聚焦面向对象设计的「套路」层——如何组织类与对象,让代码可扩展、可测试、可维护。


目录

  1. 设计模式在 Python 中的位置
  2. SOLID 五原则
  3. 创建型模式
  4. 结构型模式
  5. 行为型模式
  6. Pythonic 替代:用语言特性简化经典模式
  7. 函数式模式
  8. 模式选型与过度设计
  9. 实战:一个可维护的支付系统
  10. 速查表与最佳实践

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生成器 yield1/5
Statefunctools.partial / 枚举1/3
Command函数/functools.partial1/4
Visitorfunctools.singledispatch1/5
Mementocopy.deepcopy1/6
Factory Methoddict 映射 + 函数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
算法可替换策略函数参数
算法骨架固定模板方法父类 + 子类
类型多态处理Visitorsingledispatch
资源共享对象池@contextmanager

最佳实践:

  1. 优先用语言特性(函数、装饰器、生成器、Protocol)替代经典模式。
  2. 一个模块一个职责,类要小。
  3. 用类型注解 + Protocol 表达「依赖抽象」。
  4. 没有两种以上实现前,不要抽象。
  5. 模式是为可读性服务的,读不懂的模式就是坏模式。

一句话记忆:Python 设计模式 = SOLID 定边界 + 经典模式当工具箱 + 语言特性做简化剂——能用 yield 就不写迭代器类,能用函数参数就不建策略类。

延伸阅读

模式是经验的沉淀,不是约束的枷锁。在 Python 里,最好的「设计模式」往往是那些让代码最短、最直白、最好测的写法。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「python」更多文章

  1. Python 微服务架构:从单体拆分到服务治理
  2. Python 网络爬虫与自动化:从 requests 到 Playwright
  3. Python 库与 API 设计:从包结构到向后兼容