10、工厂方法模式:Python实现、角色组成(Product、Creator)、扩展性分析

工厂方法模式,说白了就是「把对象的创建延迟到子类」。

我刚开始接触设计模式时,总觉得工厂模式不就是 new 一下对象吗?搞那么复杂干嘛。直到我在一个项目中维护了上千行 if-else 的创建逻辑,每次加新产品都要改核心代码,改一次崩一次……嗯,从那以后,我彻底理解了工厂方法的价值。

10.1 模式的核心角色

工厂方法模式有四个核心参与者。我习惯把它们分成两组来看:

角色 名称 职责
抽象产品 Product 定义产品的公共接口
具体产品 ConcreteProduct 实现产品接口的具体类
抽象创建者 Creator 声明工厂方法,返回 Product 对象
具体创建者 ConcreteCreator 重写工厂方法,返回具体的 ConcreteProduct

你想想看,这里最关键的一点是什么?Creator 并不知道它要创建哪个具体产品。它只负责定义「怎么创建」的流程,而「创建什么」交给子类决定。

核心原则:依赖倒置。高层模块(Creator)不依赖低层模块(ConcreteProduct),两者都依赖抽象(Product)。

10.2 Python 实现:一个日志记录器的例子

我记得有一次做日志系统重构,需要支持文件日志、控制台日志、远程日志。如果用简单工厂,每次加新日志类型都要改工厂类。用工厂方法就优雅多了。

先看抽象产品:

from abc import ABC, abstractmethod

class Logger(ABC):
    """抽象产品:日志记录器"""
    
    @abstractmethod
    def log(self, message: str) -> None:
        pass

具体产品:

class FileLogger(Logger):
    def log(self, message: str) -> None:
        print(f"[文件日志] {message}")
        # 实际项目中这里会写入文件

class ConsoleLogger(Logger):
    def log(self, message: str) -> None:
        print(f"[控制台日志] {message}")

class RemoteLogger(Logger):
    def log(self, message: str) -> None:
        print(f"[远程日志] {message}")
        # 实际项目中这里会发送 HTTP 请求

抽象创建者:

class LoggerCreator(ABC):
    """抽象创建者:定义工厂方法"""
    
    @abstractmethod
    def create_logger(self) -> Logger:
        pass
    
    def write_log(self, message: str) -> None:
        # 这里可以包含公共的业务逻辑
        logger = self.create_logger()
        logger.log(message)

具体创建者:

class FileLoggerCreator(LoggerCreator):
    def create_logger(self) -> Logger:
        return FileLogger()

class ConsoleLoggerCreator(LoggerCreator):
    def create_logger(self) -> Logger:
        return ConsoleLogger()

class RemoteLoggerCreator(LoggerCreator):
    def create_logger(self) -> Logger:
        return RemoteLogger()

使用起来非常直观:

def client_code(creator: LoggerCreator) -> None:
    creator.write_log("系统启动成功")

# 客户端根据需要选择创建者
client_code(FileLoggerCreator())
client_code(ConsoleLoggerCreator())

个人习惯:我通常把 Creator 中的公共逻辑(比如 write_log 方法)做得尽量通用。这样子类只需要关注 create_logger 这一个点,职责非常清晰。

10.3 扩展性分析

工厂方法模式的扩展性,说白了就是「开闭原则」的完美体现。

扩展方式一:增加新产品

假设现在要加一个「数据库日志」:

class DatabaseLogger(Logger):
    def log(self, message: str) -> None:
        print(f"[数据库日志] {message}")

class DatabaseLoggerCreator(LoggerCreator):
    def create_logger(self) -> Logger:
        return DatabaseLogger()

看到了吗?不需要修改任何已有代码。只需要新增两个类:一个产品,一个创建者。这就是开闭原则——对扩展开放,对修改关闭。

扩展方式二:增加创建前的预处理

我曾经遇到一个场景:创建某些产品前需要做权限校验。工厂方法可以轻松应对:

class SecureFileLoggerCreator(FileLoggerCreator):
    def create_logger(self) -> Logger:
        # 权限校验逻辑
        if not self._check_permission():
            raise PermissionError("无权限创建文件日志")
        return super().create_logger()
    
    def _check_permission(self) -> bool:
        # 实际校验逻辑
        return True

我曾经踩过的坑:工厂方法虽然扩展性好,但每加一个产品就要加两个类。如果产品种类非常多(比如几十上百个),类数量会爆炸。这时候可以考虑用「反射」或「注册表」来简化。

10.4 工厂方法 vs 简单工厂

很多初学者会混淆这两个概念。我简单说下区别:

  • 简单工厂:一个工厂类里写 if-else 或 switch,根据参数返回不同产品。违反开闭原则。
  • 工厂方法:每个产品对应一个工厂子类。符合开闭原则,但类数量多。

你想想看,如果你的产品类型稳定不变,简单工厂完全够用。但如果产品经常新增或变化,工厂方法才是正道。

10.5 核心知识体系

下面这张图是我自己总结的工厂方法模式知识结构,帮你快速理清脉络:

工厂方法模式知识体系 核心角色 抽象产品 (Product) 具体产品 (ConcreteProduct) 抽象创建者 (Creator) 具体创建者 (ConcreteCreator) 扩展性分析 ✅ 增加新产品:新增两个类 ✅ 增加预处理:重写工厂方法 ✅ 符合开闭原则 ⚠️ 类数量可能膨胀 Python 实现要点 • 使用 ABC 定义抽象类 • 工厂方法返回抽象产品 • 客户端依赖抽象创建者 vs 简单工厂 • 简单工厂:一个类管所有 • 工厂方法:每个产品一个工厂 • 选择依据:产品变化频率

10.6 实际项目中的避坑指南

最后分享几个我实际项目中遇到的坑:

  • 不要滥用:如果只有一两个产品,用工厂方法反而增加复杂度。简单工厂或直接 new 更合适。
  • 注意参数传递:工厂方法如果参数太多,可以考虑用 Builder 模式配合。
  • 我曾经犯过的错:在 Creator 中写了产品相关的业务逻辑,导致子类难以复用。记住,Creator 只负责「创建流程」,不负责「产品业务」。

工厂方法模式,说白了就是「把变化封装起来」。你只要记住这个核心思想,什么时候该用它,自然就清楚了。


公众号:蓝海资料掘金营,微信deep3321