Python常用设计模式:单例、工厂与策略模式的实现

认真发呆
认真发呆
发布时间:2026-11-04 | 9人浏览过
原创

深入讲解Python日常开发中最高频的单例、工厂与策略模式的实现原理、代码范式、线程安全保障与避坑要点,配以完整流程图与选型指南。

Python常用设计模式:单例、工厂与策略模式的实现
图 1:Python 常用三大设计模式心智模型全览

设计模式是解决特定软件设计场景中反复出现问题的规范化套路。在 Python 开发中,得益于动态类型系统、模块导入缓存机制以及“函数是一等公民”的特性,很多经典设计模式的落地比静态编译语言更轻量且表达力更强。本文聚焦日常工程实践中最高频的三种设计模式:单例模式(管控全局唯一状态)、工厂模式(解除对象构建与使用的强耦合)以及策略模式(运行时灵活切换算法族),逐一剖析其核心原理、标准代码范式及避坑要点。

一、单例模式:全局唯一实例与状态共享

单例模式(Singleton Pattern)的核心目标是保证一个类在整个程序运行期间只存在一个物理实例,并提供一个全局一致的访问节点。在实际项目中,数据库连接池、全局应用配置管理器(ConfigManager)、分布式日志收集客户端等底层基础设施,通常需要全局共享统一状态并避免重复开辟连接开销,属于单例模式的标准应用场景。

1. Pythonic 方案:模块级单例

很多初学者会习惯性地在 Python 中照搬静态语言的经典实现,但事实上,Python 的模块机制本身就是最自然、最安全的天然单例。Python 在首次 import 某个模块时会执行该模块的代码,并将生成的模块对象缓存在 sys.modules 字典中。后续任何文件再次导入该模块,获取到的都是同一个模块对象与变量句柄。

# config_manager.py
class AppConfig:
    def __init__(self):
        self.settings = {"env": "production", "timeout": 30}

    def get(self, key, default=None):
        return self.settings.get(key, default)

# 模块初始化时直接实例化一次
config = AppConfig()

# 在其他业务文件中直接导入即可共享同一实例:
# from config_manager import config

2. 类级实现:基于 __new__ 与双重检查锁(Double-Checked Locking)

如果业务要求必须保留标准的类实例化语法(如调用 DatabasePool()),则需重写类的对象分配方法 __new__。为了抵御多线程并发场景下的竞争冒险,必须引入线程锁与双重检查锁定机制。

Python常用设计模式:单例、工厂与策略模式的实现
图 2:单例模式双重检查锁定(Double-Checked Locking)执行与实例交付流程
import threading

class DatabasePool:
    _instance = None
    _lock = threading.Lock()

    def __new__(cls, *args, **kwargs):
        # 一重检查:避免每次读取已存在实例时产生昂贵的加锁开销
        if cls._instance is None:
            with cls._lock:
                # 二重检查:防止多个线程同时穿过一重检查后重复实例化
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, dsn: str = "sqlite:///:memory:"):
        # 必须通过标志位阻止后续实例化时对内部属性的重复覆盖
        if not hasattr(self, "_is_initialized"):
            self.dsn = dsn
            self.connections = []
            self._is_initialized = True

# 验证效果:
db1 = DatabasePool("mysql://localhost:3306/db")
db2 = DatabasePool("postgresql://localhost:5432/db")
print(db1 is db2)  # 输出: True,内存地址一致
print(db2.dsn)     # 输出: mysql://localhost:3306/db (保留首次初始化的值)
⚠️ 关键提醒:避免 __init__ 重复执行陷阱

Python 解释器的对象创建机制规定:每次执行 ClassName(),即使 __new__ 返回的是旧实例,解释器依然会自动调用一次 __init__。若未在 __init__ 内部设置防重入标志位(如 _is_initialized),后续传入的新参数将会粗暴覆盖已有实例的状态,破坏单例的数据一致性。

二、工厂模式:对象构建解耦与协议抽象

工厂模式(Factory Pattern)旨在将“对象的创建逻辑”与“对象的使用逻辑”彻底解耦。当业务层需要根据动态传入的配置、文件扩展名或接口参数创建不同类型但对外提供相同协议的对象时,直接在业务代码中使用大量硬编码类名和 if-elif 分支会导致代码高度脆弱,违反“开闭原则”(对扩展开放,对修改关闭)。

Python常用设计模式:单例、工厂与策略模式的实现
图 3:工厂模式通过注册表分发与统一协议调用的流水线流程

1. 基于字典注册表(Registry)的 Pythonic 工厂实现

在 Python 中,类本身也是第一类对象(First-class Object)。利用类装饰器配合映射字典(Registry),可以构建无需维护任何庞大 if-elif 树的优雅工厂体系:

from abc import ABC, abstractmethod
import json
import csv
import io

# 1. 定义产品统一契约接口
class BaseExporter(ABC):
    @abstractmethod
    def export(self, records: list[dict]) -> str:
        # 执行数据导出并返回对应格式的字符串
        pass

# 2. 工厂管理类
class ExporterFactory:
    _registry: dict[str, type[BaseExporter]] = {}

    @classmethod
    def register(cls, format_name: str):
        # 装饰器:供新产品类主动自注册
        def decorator(subclass: type[BaseExporter]):
            cls._registry[format_name.lower()] = subclass
            return subclass
        return decorator

    @classmethod
    def create(cls, format_name: str, **kwargs) -> BaseExporter:
        exporter_class = cls._registry.get(format_name.lower())
        if not exporter_class:
            valid_types = ", ".join(cls._registry.keys())
            raise ValueError(f"不支持的导出格式: '{format_name}'。支持格式: [{valid_types}]")
        return exporter_class(**kwargs)

# 3. 编写具体产品并直接标记注册
@ExporterFactory.register("json")
class JSONExporter(BaseExporter):
    def export(self, records: list[dict]) -> str:
        return json.dumps(records, ensure_ascii=False, indent=2)

@ExporterFactory.register("csv")
class CSVExporter(BaseExporter):
    def export(self, records: list[dict]) -> str:
        if not records:
            return ""
        output = io.StringIO()
        writer = csv.DictWriter(output, fieldnames=records[0].keys())
        writer.writeheader()
        writer.writerows(records)
        return output.getvalue()

# 4. 业务层统一调用:完全隔离产品具体类型
data = [{"name": "Alice", "role": "Admin"}, {"name": "Bob", "role": "Dev"}]
chosen_format = "csv"  # 可来自用户输入或HTTP请求头
exporter = ExporterFactory.create(chosen_format)
result = exporter.export(data)
print(result)
💡 工厂扩展性说明

当未来需要新增 XML 或 YAML 导出器时,开发者只需在独立模块中编写继承 BaseExporter 的新类并打上 @ExporterFactory.register("yaml") 标签,核心业务逻辑无需改动哪怕一行代码。

三、策略模式:算法封装与运行时自由替换

策略模式(Strategy Pattern)属于行为型模式,其核心思想是:定义一系列算法或业务规则,把它们各自封装起来,并且使它们之间可以相互替换。策略模式让算法的变化独立于使用算法的客户端。

典型的反模式是在一个核心业务函数中堆砌厚重的 if-elif-else 分支来处理不同计算规则(如电商促销打折、不同会员等级运费计算、排序过滤算法等)。此类写法不仅难以阅读,而且每次增删规则都要冒着破坏已有分支的风险。

Python常用设计模式:单例、工厂与策略模式的实现
图 4:策略模式的业务上下文与算法族解耦架构

1. 函数式一等公民策略(轻量优雅)与面向对象策略(适用于复杂状态)

在 Python 中,函数本身可以直接作为参数传递。对于简单的计算逻辑,直接使用 Callable 签名作为策略规范,能够完全免去声明空壳类的样板代码。

from typing import Callable
from decimal import Decimal

# 1. 策略契约:入参为原始金额,返回计算后最终金额
DiscountStrategy = Callable[[Decimal], Decimal]

# 2. 具体算法策略定义(纯函数式)
def normal_discount(amount: Decimal) -> Decimal:
    # 无优惠原价计算
    return amount

def vip_percentage_discount(amount: Decimal) -> Decimal:
    # VIP会员专享 8.5 折
    return (amount * Decimal("0.85")).quantize(Decimal("0.01"))

def create_threshold_discount(threshold: Decimal, deduction: Decimal) -> DiscountStrategy:
    # 闭包策略工厂:构造满足特定满减条件的策略
    def strategy(amount: Decimal) -> Decimal:
        if amount >= threshold:
            return max(Decimal("0"), amount - deduction)
        return amount
    return strategy

# 3. 业务上下文:持有一笔订单与当前挂载的折扣策略
class OrderContext:
    def __init__(self, order_id: str, amount: Decimal, strategy: DiscountStrategy = normal_discount):
        self.order_id = order_id
        self.amount = amount
        self.strategy = strategy

    def set_strategy(self, new_strategy: DiscountStrategy):
        # 运行时热插拔切换算法
        self.strategy = new_strategy

    def get_final_price(self) -> Decimal:
        return self.strategy(self.amount)

# 4. 运行时验证
order = OrderContext(order_id="ORD-2026-99", amount=Decimal("300.00"))
print(f"默认原价: {order.get_final_price()}")  # 300.00

# 切换为 VIP 折扣
order.set_strategy(vip_percentage_discount)
print(f"VIP折扣价: {order.get_final_price()}")  # 255.00

# 切换为满 200 减 50 促销策略
order.set_strategy(create_threshold_discount(threshold=Decimal("200"), deduction=Decimal("50")))
print(f"满减折扣价: {order.get_final_price()}")  # 250.00

四、模式选型决策树与综合对比

在工程重构中,设计模式并非使用得越多越好。引入任何模式都伴随着间接层与理解成本的增加。明确识别软件面临的核心痛点,是合理选型的关键前置条件。

Python常用设计模式:单例、工厂与策略模式的实现
图 5:三大设计模式选型决策路径与核心维度对比
模式名称 模式归类 核心职责 Pythonic 落地推荐 典型反模式与误用
单例模式 (Singleton) 创建型 约束实例总数为 1,提供全局共享访问点 模块导入级单例;复杂需求使用 __new__ + Lock 滥用单例当作全局变量传递杂乱参数,导致单元测试互相污染
工厂模式 (Factory) 创建型 隔离复杂对象的实例化过程,统一对外协议接口 基于字典注册表装饰器(Registry Pattern)实现自动挂载 仅有一两个简单类且不会变动时强行引入厚重工厂层
策略模式 (Strategy) 行为型 将可变的行为规则独立封装,支持运行时动态热替换 利用函数作为一等公民,直接使用 Callable 闭包传递 为原本固定的逻辑编写大量只调用一次的空壳策略类

结语:务实应用设计模式

遵循 Python 哲学的核心准则:“Simple is better than complex”。在面对全局共享需求时,优先尝试天然且线程安全的模块单例;在面临多类型动态分发时,运用注册表字典将工厂分支降至 O(1) 复杂度;在面对复杂计算规则分支时,善用 Callable 策略函数。只有当现有代码结构阻碍了扩展或引发了真实的代码重复痛点时,重构引入设计模式才能发挥其最大工程价值。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表