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())
问题在于:
- 每增加一种动物(如
Bird),就要改get_pet的 if-else,违反开闭原则(对扩展开放,对修改关闭)。 - 创建逻辑散落:如果创建
Dog需要很多步骤(读配置、连接资源),这些代码会重复或混乱。 - 调用方依赖具体类:虽然这里用字符串传参,但“创建谁”的逻辑仍然和
Dog、Cat绑在一起。
工厂模式就是把“创建谁、怎么创建”放进工厂里,调用方只和“产品抽象”或“工厂接口”打交道。
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()) # 叽叽
新增一种动物(如鸭子)时:只需新增 Duck 和 DuckFactory,不用改 DogFactory、CatFactory 等原有代码,这就是对扩展开放、对修改关闭。
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,这样对工厂模式会掌握得比较扎实。