python设计模式–工厂模式(Factory Pattern)

Python 设计模式——工厂模式(Factory Pattern)详解

本文面向零基础新手,从概念到三种工厂、从简单示例到完整项目用法,全方位讲解工厂模式。


一、什么是工厂模式?

1.1 通俗理解

工厂模式是一种创建型设计模式,核心思想是:

不要把“创建对象”的代码散落得到处都是,而是集中到一个地方(工厂)来创建;调用方只需要告诉工厂“我要什么”,不用关心对象是怎么被造出来的。

可以这样类比:

  • 不用工厂:你想吃披萨,得自己买面粉、和面、发酵、烤制……(到处写 new 具体类,和具体实现紧耦合。)
  • 用工厂:你只要跟店员说“我要一份海鲜披萨”,后厨(工厂)负责做好给你。(调用方只依赖“产品接口”或“工厂接口”,不依赖具体类。)

所以,工厂模式解决的是:“创建对象”的职责集中化、以及让调用方尽量不依赖具体实现类

1.2 为什么需要工厂?

先看一段没有工厂时的典型问题:

# 没有工厂:调用方直接 new 具体类,和实现紧耦合
class Dog:
    def speak(self):
        return "汪汪"

class Cat:
    def speak(self):
        return "喵喵"

# 业务代码里到处是 if-else + 具体类名
def get_pet(animal_type):
    if animal_type == "dog":
        return Dog()
    elif animal_type == "cat":
        return Cat()
    else:
        raise ValueError("未知类型")

pet = get_pet("dog")
print(pet.speak())

问题在于:

  1. 每增加一种动物(如 Bird),就要改 get_pet 的 if-else,违反开闭原则(对扩展开放,对修改关闭)。
  2. 创建逻辑散落:如果创建 Dog 需要很多步骤(读配置、连接资源),这些代码会重复或混乱。
  3. 调用方依赖具体类:虽然这里用字符串传参,但“创建谁”的逻辑仍然和 DogCat 绑在一起。

工厂模式就是把“创建谁、怎么创建”放进工厂里,调用方只和“产品抽象”或“工厂接口”打交道。

1.3 工厂模式的三种形态

类型 通俗说法 主要作用
简单工厂 一个方法根据参数返回不同产品 把创建逻辑集中到一个函数/类里,调用方不直接 new 具体类
工厂方法 每个产品有各自的“工厂子类” 把“创建哪种产品”延迟到子类,符合开闭原则
抽象工厂 生产“一族”相关产品 一次创建多个相关对象(如 UI 主题:按钮+文本框+边框)

下面从简单到复杂,逐个用 Python 示例说明。


二、简单工厂(Simple Factory)

2.1 概念

简单工厂:有一个“工厂”(类或函数),根据参数(如字符串、枚举)决定创建并返回哪一种具体产品。调用方只依赖“产品类型”或“产品接口”,不写 Dog()Cat() 这种具体类名。

2.2 结构示意

调用方  →  工厂.create(类型)  →  返回  具体产品(Dog / Cat / ...)
                ↑
           根据类型 new 不同类

2.3 示例:动物工厂

第一步:定义“产品”抽象(接口)

用抽象基类表示“动物”这一族产品,具体动物都实现它。

from abc import ABC, abstractmethod

class Animal(ABC):
    """动物的抽象基类:所有具体动物都继承它"""
    @abstractmethod
    def speak(self):
        pass

第二步:定义具体产品

class Dog(Animal):
    def speak(self):
        return "汪汪"

class Cat(Animal):
    def speak(self):
        return "喵喵"

class Bird(Animal):
    def speak(self):
        return "叽叽"

第三步:简单工厂——根据参数创建对象

class AnimalFactory:
    """简单工厂:根据类型字符串创建对应动物"""
    @staticmethod
    def create(animal_type: str) -> Animal:
        if animal_type == "dog":
            return Dog()
        elif animal_type == "cat":
            return Cat()
        elif animal_type == "bird":
            return Bird()
        else:
            raise ValueError(f"不支持的动物类型: {animal_type}")

# 使用方式:调用方不出现 Dog、Cat 等具体类名
pet = AnimalFactory.create("dog")
print(pet.speak())  # 汪汪

pet2 = AnimalFactory.create("cat")
print(pet2.speak())  # 喵喵

也可以用“工厂函数”代替工厂类(更简单):

def create_animal(animal_type: str) -> Animal:
    if animal_type == "dog":
        return Dog()
    elif animal_type == "cat":
        return Cat()
    elif animal_type == "bird":
        return Bird()
    raise ValueError(f"不支持的动物类型: {animal_type}")

pet = create_animal("bird")
print(pet.speak())  # 叽叽

2.4 简单工厂的优缺点

  • 优点:创建逻辑集中,调用方不依赖具体类,代码清晰。
  • 缺点:每增加一种产品(如 Duck),就要改工厂里的 if-else,违反开闭原则。所以简单工厂适合“产品种类相对稳定”的场景。

三、工厂方法模式(Factory Method)

3.1 概念

工厂方法:把“创建具体产品”这件事放到子类里去做。父类(或接口)只定义“有一个方法会返回产品”,不写死具体类型;每个具体工厂子类负责创建一种具体产品。这样新增产品 = 新增一个工厂子类,不用改原有工厂代码,符合开闭原则。

3.2 结构示意

抽象工厂(定义 create 方法) 
    ├── DogFactory  →  create() 返回 Dog
    ├── CatFactory  →  create() 返回 Cat
    └── BirdFactory →  create() 返回 Bird

3.3 示例:动物工厂方法

第一步:产品抽象 + 具体产品(同上)

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        return "汪汪"

class Cat(Animal):
    def speak(self):
        return "喵喵"

class Bird(Animal):
    def speak(self):
        return "叽叽"

第二步:抽象工厂 + 具体工厂(每个工厂只生产一种产品)

class AnimalFactory(ABC):
    """抽象工厂:只声明“有一个方法会返回动物”"""
    @abstractmethod
    def create_animal(self) -> Animal:
        pass

class DogFactory(AnimalFactory):
    def create_animal(self) -> Animal:
        return Dog()

class CatFactory(AnimalFactory):
    def create_animal(self) -> Animal:
        return Cat()

class BirdFactory(AnimalFactory):
    def create_animal(self) -> Animal:
        return Bird()

第三步:使用——依赖抽象工厂,不依赖具体产品类

def main(factory: AnimalFactory):
    animal = factory.create_animal()
    print(animal.speak())

main(DogFactory())   # 汪汪
main(CatFactory())   # 喵喵
main(BirdFactory())  # 叽叽

新增一种动物(如鸭子)时:只需新增 DuckDuckFactory不用改 DogFactoryCatFactory 等原有代码,这就是对扩展开放、对修改关闭

3.4 工厂方法小结

  • 优点:符合开闭原则,新增产品只需加新类;每个工厂职责单一。
  • 缺点:类会变多(每个产品一个工厂类);调用方需要先选“用哪个工厂”。

四、抽象工厂模式(Abstract Factory)

4.1 概念

抽象工厂:用来创建一族相关或相互依赖的对象,而不是单个对象。例如:

  • 一套 UI 主题:要同时创建“按钮 + 文本框 + 边框”,且风格一致(都是“暗色”或都是“亮色”)。
  • 一套数据库访问:要同时创建“连接 + 命令 + 读取器”,且属于同一套实现(如 MySQL 或 SQLite)。

所以抽象工厂是“工厂的工厂”:一个具体工厂实现类,能创建多个相关产品。

4.2 结构示意

抽象工厂(定义:创建按钮、创建文本框、创建边框)
    ├── DarkThemeFactory  →  暗色按钮、暗色文本框、暗色边框
    └── LightThemeFactory →  亮色按钮、亮色文本框、亮色边框

4.3 示例:UI 主题(按钮 + 文本框)

第一步:定义“产品族”的抽象——按钮、文本框

from abc import ABC, abstractmethod

class Button(ABC):
    @abstractmethod
    def render(self):
        pass

class TextBox(ABC):
    @abstractmethod
    def render(self):
        pass

第二步:两套具体产品——暗色 / 亮色

class DarkButton(Button):
    def render(self):
        return "[暗色按钮]"

class DarkTextBox(TextBox):
    def render(self):
        return "暗色文本框"

class LightButton(Button):
    def render(self):
        return "[亮色按钮]"

class LightTextBox(TextBox):
    def render(self):
        return "亮色文本框"

第三步:抽象工厂——能创建“按钮 + 文本框”一族

class GUIFactory(ABC):
    """抽象工厂:能创建一整套 UI 组件"""
    @abstractmethod
    def create_button(self) -> Button:
        pass

    @abstractmethod
    def create_textbox(self) -> TextBox:
        pass

第四步:具体工厂——暗色主题 / 亮色主题

class DarkThemeFactory(GUIFactory):
    def create_button(self) -> Button:
        return DarkButton()

    def create_textbox(self) -> TextBox:
        return DarkTextBox()

class LightThemeFactory(GUIFactory):
    def create_button(self) -> Button:
        return LightButton()

    def create_textbox(self) -> TextBox:
        return LightTextBox()

第五步:使用——一次拿到一整族产品

def build_ui(factory: GUIFactory):
    button = factory.create_button()
    textbox = factory.create_textbox()
    print(button.render(), textbox.render())

build_ui(DarkThemeFactory())   # [暗色按钮] 暗色文本框
build_ui(LightThemeFactory()) # [亮色按钮] 亮色文本框

4.4 抽象工厂小结

  • 优点:保证一组对象风格一致(同一主题、同一数据库实现);切换主题/实现只需换一个具体工厂。
  • 缺点:新增“产品种类”(如再加一个“复选框”)需要改抽象工厂和所有具体工厂;类会更多。

五、三种工厂对比(何时用哪种?)

维度 简单工厂 工厂方法 抽象工厂
创建对象数量 单个 单个 一族(多个相关)
扩展方式 改工厂内部 if-else 新增工厂子类 + 产品子类 新增具体工厂(一族产品)
开闭原则 易违反(常改工厂) 符合 符合(对产品族)
复杂度
典型场景 产品种类少、变化少 产品种类可能增多、希望少改旧代码 多套“主题/实现”且每套有多个相关对象

新手建议:先掌握简单工厂工厂方法;遇到“要一次创建多个相关对象且要保证一致”时,再考虑抽象工厂。


六、完整示例:支付方式工厂(简单工厂 + 工厂方法结合)

需求:根据支付类型(支付宝、微信、银行卡)创建对应的支付对象,并调用统一接口 pay(money)

产品抽象 + 具体产品:

from abc import ABC, abstractmethod

class Payment(ABC):
    @abstractmethod
    def pay(self, money: float) -> str:
        pass

class AlipayPayment(Payment):
    def pay(self, money: float) -> str:
        return f"支付宝支付 {money} 元"

class WechatPayment(Payment):
    def pay(self, money: float) -> str:
        return f"微信支付 {money} 元"

class BankPayment(Payment):
    def pay(self, money: float) -> str:
        return f"银行卡支付 {money} 元"

简单工厂实现:

class PaymentFactory:
    @staticmethod
    def create(method: str) -> Payment:
        if method == "alipay":
            return AlipayPayment()
        elif method == "wechat":
            return WechatPayment()
        elif method == "bank":
            return BankPayment()
        raise ValueError(f"不支持的支付方式: {method}")

# 使用
p = PaymentFactory.create("alipay")
print(p.pay(100))  # 支付宝支付 100 元

工厂方法实现(便于以后扩展更多支付方式):

class PaymentFactory(ABC):
    @abstractmethod
    def create_payment(self) -> Payment:
        pass

class AlipayFactory(PaymentFactory):
    def create_payment(self) -> Payment:
        return AlipayPayment()

class WechatFactory(PaymentFactory):
    def create_payment(self) -> Payment:
        return WechatPayment()

class BankFactory(PaymentFactory):
    def create_payment(self) -> Payment:
        return BankPayment()

# 使用
factory = AlipayFactory()
p = factory.create_payment()
print(p.pay(99))  # 支付宝支付 99 元

七、常见问题与注意点

7.1 简单工厂里用字典代替 if-else(更易扩展)

class AnimalFactory:
    _creators = {
        "dog": lambda: Dog(),
        "cat": lambda: Cat(),
        "bird": lambda: Bird(),
    }

    @classmethod
    def create(cls, animal_type: str) -> Animal:
        if animal_type not in cls._creators:
            raise ValueError(f"不支持的动物类型: {animal_type}")
        return cls._creators[animal_type]()

这样新增动物时,只需在 _creators 里加一行,不必改 create 的逻辑(相对更接近开闭原则)。

7.2 工厂方法里“谁来决定用哪个工厂?”

通常由配置、用户选择、或上层模块决定。例如:

def get_factory(theme: str) -> GUIFactory:
    if theme == "dark":
        return DarkThemeFactory()
    return LightThemeFactory()

factory = get_factory("dark")
build_ui(factory)

7.3 不要滥用工厂

  • 如果只有一两个具体类、且几乎不会变,直接 new 可能更简单。
  • 工厂适合:创建逻辑复杂、有多分支、或希望调用方不依赖具体类的场景。

八、小结

  • 简单工厂:一个工厂方法根据参数返回不同产品,实现简单,扩展时易改工厂内部。
  • 工厂方法:每个具体工厂只生产一种产品,扩展靠新增类,符合开闭原则。
  • 抽象工厂:一个工厂生产一族相关产品,保证风格一致,适合主题、数据库实现等。

建议先多写几遍简单工厂和工厂方法的示例,再尝试用抽象工厂做一个小主题切换 Demo,这样对工厂模式会掌握得比较扎实。

发表评论