今天我们来聊一个在实际开发中极度常用,能让你的代码彻底摆脱 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
class MessageSender(ABC): @abstractmethod def send(self, message: str, to: str): pass
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}"
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)
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): 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: root = ET.fromstring(data) return {child.tag: child.text for child in root}
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)
class YAMLParser(DocumentParser): def parse(self, data: str) -> dict: 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}")
|