今天我们来聊一个在实际开发中极度常用,能让你的代码彻底摆脱 if-elif-else 泥潭的经典创建型模式——工厂方法模式(Factory Method Pattern)。

Day 2:工厂方法模式 (Factory Method Pattern)

1. 它是怎么来的?

在写代码时,我们经常需要创建对象。最直接的方法就是用 new(在 Python 中是 Class())。
比如你正在开发一个物流管理系统,刚开始业务简单,只有“卡车”送货,你满脑子都是:

1
2
transport = Truck()
transport.deliver()

但很快,业务扩大,客户要求增加“轮船”送货。你不得不去修改原有的代码,加上判断逻辑:

1
2
3
4
5
if mode == "road":
transport = Truck()
elif mode == "sea":
transport = Ship()
# 以后要是再加“飞机”、“高铁”怎么办?代码会变得越来越臃肿,严重违反“开闭原则”(对扩展开放,对修改关闭)。

工厂方法模式就是为了解决这个问题而生的。
它主张:不要在业务代码中直接实例化具体产品,而是定义一个创建对象的接口(工厂),让子类决定实例化哪一个具体类。
这样,新增产品时,你只需要增加一个新的工厂子类,而不需要修改既有的业务逻辑。

2. 适用场景

  • 无法预知对象类型:当一个类不知道它所必须创建的对象的类的时候。
  • 解耦创建与使用:你想将产品的“创建逻辑”与“使用逻辑”分离开,让系统更易于扩展。
  • 统一管理复杂对象的创建:如果对象的创建过程非常复杂(需要初始化很多参数、配置),可以用工厂统一封装。

3. 优缺点分析

优点 缺点
符合开闭原则:新增一种产品时,只需新增具体产品类和对应的工厂类,无需修改现有代码。 代码复杂度增加:每增加一个产品,都需要增加一个具体类和一个工厂类,类的数量成倍增长。
单一职责原则:将创建产品的代码搬到了工厂中,使得业务代码职责更单一。
解耦:调用者只需要知道产品的抽象接口和工厂,不需要关心具体产品的实现细节。

4. Python 代码实现

Python 是一门动态语言,其实不像 Java/C++ 那样高度依赖严格的接口继承,但用 Python 实现标准的工厂方法模式,能让我们更清晰地理解它的设计精髓。

💡 简单实例:消息推送器

假设我们要实现一个消息推送系统,支持短信(SMS)和邮件(Email)推送。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from abc import ABC, abstractmethod

# ================= 1. 抽象产品 (Product) =================
class MessageSender(ABC):
@abstractmethod
def send(self, message: str, to: str):
pass

# ================= 2. 具体产品 (Concrete Products) =================
class SMSSender(MessageSender):
def send(self, message: str, to: str):
return f"[短信推送] 发送给 {to}: {message}"

class EmailSender(MessageSender):
def send(self, message: str, to: str):
return f"[邮件推送] 发送给 {to}: {message}"


# ================= 3. 抽象工厂 (Creator) =================
class SenderFactory(ABC):
@abstractmethod
def create_sender(self) -> MessageSender:
"""工厂方法,由子类来实现具体创建哪种产品"""
pass

def notify(self, message: str, to: str):
"""
核心业务逻辑:工厂不仅能创建对象,通常还会包含一些使用该对象的公共逻辑。
"""
sender = self.create_sender()
return sender.send(message, to)


# ================= 4. 具体工厂 (Concrete Creators) =================
class SMSSenderFactory(SenderFactory):
def create_sender(self) -> MessageSender:
return SMSSender()

class EmailSenderFactory(SenderFactory):
def create_sender(self) -> MessageSender:
return EmailSender()


# ================= 客户端调用 =================
def client_code(factory: SenderFactory, message: str, to: str):
# 客户端只跟抽象工厂和抽象产品打交道,完全不知道具体是 SMS 还是 Email
result = factory.notify(message, to)
print(result)

# 如果想发送短信
client_code(SMSSenderFactory(), "您的验证码是 1234", "13800138000")
# 如果想发送邮件
client_code(EmailSenderFactory(), "欢迎订阅我们的周报", "user@example.com")

🚀 复杂实例:动态多格式文档解析器

在实际的工业级项目中,我们常常结合 Python 的动态特性,用类注册表的方式来实现一个更高效、不臃肿的工厂方法。
下面是一个多格式文档解析器(支持 JSON、XML、YAML)。我们将所有的具体解析器(产品)和它们对应的工厂动态注册,避免写出大量冗余的工厂类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
from abc import ABC, abstractmethod
import json
import xml.etree.ElementTree as ET

# ================= 抽象产品 =================
class DocumentParser(ABC):
@abstractmethod
def parse(self, data: str) -> dict:
pass

# ================= 具体产品 =================
class JSONParser(DocumentParser):
def parse(self, data: str) -> dict:
return json.loads(data)

class XMLParser(DocumentParser):
def parse(self, data: str) -> dict:
# 简单模拟 XML 转 dict
root = ET.fromstring(data)
return {child.tag: child.text for child in root}

# ================= 智能动态工厂 (结合 Python 动态特性) =================
class ParserFactory:
"""
通过注册机制实现的动态工厂,避免了为每个产品都写一个 Factory 子类的繁琐。
"""
_parsers = {}

@classmethod
def register_parser(cls, format_type: str, parser_class):
"""允许在运行时动态注册新的解析器"""
cls._parsers[format_type.lower()] = parser_class

@classmethod
def get_parser(cls, format_type: str) -> DocumentParser:
parser_class = cls._parsers.get(format_type.lower())
if not parser_class:
raise ValueError(f"不支持的文档格式: {format_type}")
return parser_class() # 实例化并返回


# ================= 注册现有产品 =================
ParserFactory.register_parser("json", JSONParser)
ParserFactory.register_parser("xml", XMLParser)


# ================= 扩展新产品(不修改原有工厂代码) =================
# 假设业务新增了 YAML 格式,我们只需要定义产品并注册即可
class YAMLParser(DocumentParser):
def parse(self, data: str) -> dict:
# 模拟 yaml 解析
return {"status": "parsed from yaml", "content": data}

# 动态注册
ParserFactory.register_parser("yaml", YAMLParser)


# ================= 客户端测试 =================
if __name__ == "__main__":
raw_json = '{"name": "Alice", "age": 25}'
raw_xml = '<user><name>Bob</name><age>30</age></user>'
raw_yaml = 'name: Charlie\nage: 35'

# 统一通过工厂获取解析器,客户端无需关心具体类名
for fmt, data in [("json", raw_json), ("xml", raw_xml), ("yaml", raw_yaml)]:
parser = ParserFactory.get_parser(fmt)
parsed_data = parser.parse(data)
print(f"[{fmt.upper()} 解析结果]: {parsed_data}")