pin_drop当前位置:知识文库 ❯ 图文
Python工厂模式完全指南 - 三种工厂实现详解
一、什么是工厂模式
工厂模式(Factory Pattern)是创建型设计模式中最常用的模式之一。它的核心思想是:将对象的创建过程封装起来,调用者无需知道具体的创建细节,只需要通过工厂获取所需对象即可。
工厂模式分为三种主要类型:简单工厂模式、工厂方法模式和抽象工厂模式。这三种模式的复杂度递增,适用的场景也有所不同。
工厂模式的核心优势
-
解耦合:客户端代码与具体类解耦,只依赖抽象接口
-
可扩展:新增产品类型时,只需添加新的工厂类,符合开闭原则
-
集中管理:对象创建逻辑集中在一处,便于维护和修改
-
延迟实例化:可以动态决定创建哪个具体类
二、简单工厂模式
简单工厂模式是最基础的工厂模式,通过一个工厂类根据参数创建不同的产品对象。
1. 基础实现
代码示例
from abc import ABC, abstractmethod
# 产品基类
class Payment(ABC):
@abstractmethod
def pay(self, amount):
pass
# 具体产品
class Alipay(Payment):
def pay(self, amount):
print(f"支付宝支付: {amount}元")
class WechatPay(Payment):
def pay(self, amount):
print(f"微信支付: {amount}元")
class BankPay(Payment):
def pay(self, amount):
print(f"银行卡支付: {amount}元")
# 简单工厂
class PaymentFactory:
@staticmethod
def create_payment(method):
payment_map = {
"alipay": Alipay,
"wechat": WechatPay,
"bank": BankPay
}
if method not in payment_map:
raise ValueError(f"不支持的支付方式: {method}")
return payment_map[method]()
# 使用工厂
factory = PaymentFactory()
payment = factory.create_payment("alipay")
payment.pay(100) # 支付宝支付: 100元
payment2 = factory.create_payment("wechat")
payment2.pay(200) # 微信支付: 200元
2. 使用字典映射的优化实现
代码示例
class PaymentFactory:
"""使用注册机制的简单工厂"""
_payments = {}
@classmethod
def register(cls, name):
def decorator(payment_cls):
cls._payments[name] = payment_cls
return payment_cls
return decorator
@classmethod
def create(cls, method, **kwargs):
if method not in cls._payments:
raise ValueError(f"不支持的支付方式: {method}")
return cls._payments[method](**kwargs)
# 注册支付方式
@PaymentFactory.register("alipay")
class Alipay:
def pay(self, amount):
print(f"支付宝支付: {amount}元")
@PaymentFactory.register("wechat")
class WechatPay:
def pay(self, amount):
print(f"微信支付: {amount}元")
# 使用
payment = PaymentFactory.create("alipay")
payment.pay(50)
三、工厂方法模式
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化哪个类。它将实例化延迟到子类中,符合开闭原则。
1. 基础实现
代码示例
from abc import ABC, abstractmethod
# 产品基类
class Document(ABC):
@abstractmethod
def open(self):
pass
@abstractmethod
def save(self):
pass
# 具体产品
class WordDocument(Document):
def open(self):
print("打开Word文档")
def save(self):
print("保存为.docx格式")
class PDFDocument(Document):
def open(self):
print("打开PDF文档")
def save(self):
print("保存为.pdf格式")
class ExcelDocument(Document):
def open(self):
print("打开Excel文档")
def save(self):
print("保存为.xlsx格式")
# 工厂基类
class DocumentFactory(ABC):
@abstractmethod
def create_document(self) -> Document:
pass
# 具体工厂
class WordFactory(DocumentFactory):
def create_document(self) -> Document:
return WordDocument()
class PDFFactory(DocumentFactory):
def create_document(self) -> Document:
return PDFDocument()
class ExcelFactory(DocumentFactory):
def create_document(self) -> Document:
return ExcelDocument()
# 使用
def process_document(factory: DocumentFactory):
doc = factory.create_document()
doc.open()
doc.save()
process_document(WordFactory())
process_document(PDFFactory())
2. 使用注册表模式的工厂方法
代码示例
class DocumentRegistry:
"""文档工厂注册表"""
_factories = {}
@classmethod
def register(cls, doc_type):
def decorator(factory_cls):
cls._factories[doc_type] = factory_cls
return factory_cls
return decorator
@classmethod
def create(cls, doc_type):
if doc_type not in cls._factories:
raise ValueError(f"不支持的文档类型: {doc_type}")
return cls._factories[doc_type]().create_document()
# 注册工厂
@DocumentRegistry.register("word")
class WordFactory:
def create_document(self):
return WordDocument()
@DocumentRegistry.register("pdf")
class PDFFactory:
def create_document(self):
return PDFDocument()
# 使用
doc = DocumentRegistry.create("word")
doc.open()
doc.save()
四、抽象工厂模式
抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。它适用于创建产品族的场景。
1. UI组件工厂示例
代码示例
from abc import ABC, abstractmethod
# 产品族A - 按钮
class Button(ABC):
@abstractmethod
def render(self):
pass
class WindowsButton(Button):
def render(self):
print("渲染Windows风格按钮")
class MacButton(Button):
def render(self):
print("渲染Mac风格按钮")
class LinuxButton(Button):
def render(self):
print("渲染Linux风格按钮")
# 产品族B - 输入框
class InputBox(ABC):
@abstractmethod
def render(self):
pass
class WindowsInputBox(InputBox):
def render(self):
print("渲染Windows风格输入框")
class MacInputBox(InputBox):
def render(self):
print("渲染Mac风格输入框")
class LinuxInputBox(InputBox):
def render(self):
print("渲染Linux风格输入框")
# 产品族C - 对话框
class Dialog(ABC):
@abstractmethod
def show(self):
pass
class WindowsDialog(Dialog):
def show(self):
print("显示Windows风格对话框")
class MacDialog(Dialog):
def show(self):
print("显示Mac风格对话框")
class LinuxDialog(Dialog):
def show(self):
print("显示Linux风格对话框")
# 抽象工厂
class UIFactory(ABC):
@abstractmethod
def create_button(self) -> Button:
pass
@abstractmethod
def create_input_box(self) -> InputBox:
pass
@abstractmethod
def create_dialog(self) -> Dialog:
pass
# 具体工厂
class WindowsUIFactory(UIFactory):
def create_button(self) -> Button:
return WindowsButton()
def create_input_box(self) -> InputBox:
return WindowsInputBox()
def create_dialog(self) -> Dialog:
return WindowsDialog()
class MacUIFactory(UIFactory):
def create_button(self) -> Button:
return MacButton()
def create_input_box(self) -> InputBox:
return MacInputBox()
def create_dialog(self) -> Dialog:
return MacDialog()
class LinuxUIFactory(UIFactory):
def create_button(self) -> Button:
return LinuxButton()
def create_input_box(self) -> InputBox:
return LinuxInputBox()
def create_dialog(self) -> Dialog:
return LinuxDialog()
# 使用工厂
def create_ui(factory: UIFactory):
button = factory.create_button()
input_box = factory.create_input_box()
dialog = factory.create_dialog()
button.render()
input_box.render()
dialog.show()
# 创建Windows风格UI
print("=== Windows UI ===")
create_ui(WindowsUIFactory())
# 创建Mac风格UI
print("\n=== Mac UI ===")
create_ui(MacUIFactory())
2. 数据库连接抽象工厂
代码示例
from abc import ABC, abstractmethod
# 数据库连接产品族
class Connection(ABC):
@abstractmethod
def connect(self):
pass
class Query(ABC):
@abstractmethod
def execute(self, sql):
pass
class Transaction(ABC):
@abstractmethod
def begin(self):
pass
@abstractmethod
def commit(self):
pass
# MySQL产品族
class MySQLConnection(Connection):
def connect(self):
print("连接到MySQL数据库")
return self
class MySQLQuery(Query):
def execute(self, sql):
print(f"执行MySQL查询: {sql}")
class MySQLTransaction(Transaction):
def begin(self):
print("开始MySQL事务")
def commit(self):
print("提交MySQL事务")
# PostgreSQL产品族
class PostgreSQLConnection(Connection):
def connect(self):
print("连接到PostgreSQL数据库")
return self
class PostgreSQLQuery(Query):
def execute(self, sql):
print(f"执行PostgreSQL查询: {sql}")
class PostgreSQLTransaction(Transaction):
def begin(self):
print("开始PostgreSQL事务")
def commit(self):
print("提交PostgreSQL事务")
# 抽象工厂
class DatabaseFactory(ABC):
@abstractmethod
def create_connection(self) -> Connection:
pass
@abstractmethod
def create_query(self) -> Query:
pass
@abstractmethod
def create_transaction(self) -> Transaction:
pass
# 具体工厂
class MySQLFactory(DatabaseFactory):
def create_connection(self):
return MySQLConnection()
def create_query(self):
return MySQLQuery()
def create_transaction(self):
return MySQLTransaction()
class PostgreSQLFactory(DatabaseFactory):
def create_connection(self):
return PostgreSQLConnection()
def create_query(self):
return PostgreSQLQuery()
def create_transaction(self):
return PostgreSQLTransaction()
# 使用
def init_database(factory: DatabaseFactory):
conn = factory.create_connection()
conn.connect()
query = factory.create_query()
query.execute("SELECT * FROM users")
tx = factory.create_transaction()
tx.begin()
tx.commit()
init_database(MySQLFactory())
五、Python中的工厂模式最佳实践
Python的动态特性让工厂模式的实现更加灵活。以下是一些Python特有的最佳实践。
1. 使用函数作为工厂
代码示例
# Python中函数本身就是一等公民,可以直接作为工厂
def create_animal(animal_type, **kwargs):
"""使用函数实现工厂模式"""
animals = {
"dog": lambda: Dog(**kwargs),
"cat": lambda: Cat(**kwargs),
"bird": lambda: Bird(**kwargs),
}
if animal_type not in animals:
raise ValueError(f"未知动物类型: {animal_type}")
return animals[animal_type]()
class Dog:
def __init__(self, name="旺财"):
self.name = name
def speak(self):
return f"{self.name}: 汪汪!"
class Cat:
def __init__(self, name="咪咪"):
self.name = name
def speak(self):
return f"{self.name}: 喵喵!"
class Bird:
def __init__(self, name="小鸟"):
self.name = name
def speak(self):
return f"{self.name}: 啾啾!"
# 使用
dog = create_animal("dog", name="大黄")
print(dog.speak()) # 大黄: 汪汪!
2. 使用类方法作为工厂
代码示例
from datetime import datetime, date
class DateUtil:
"""使用类方法实现工厂方法模式"""
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
@classmethod
def from_string(cls, date_str):
"""从字符串创建日期"""
parts = date_str.split("-")
return cls(int(parts[0]), int(parts[1]), int(parts[2]))
@classmethod
def from_timestamp(cls, timestamp):
"""从时间戳创建日期"""
dt = datetime.fromtimestamp(timestamp)
return cls(dt.year, dt.month, dt.day)
@classmethod
def today(cls):
"""创建今天的日期"""
t = datetime.now()
return cls(t.year, t.month, t.day)
def __str__(self):
return f"{self.year}-{self.month:02d}-{self.day:02d}"
# 使用不同的工厂方法
d1 = DateUtil.from_string("2026-06-12")
d2 = DateUtil.from_timestamp(1718179200)
d3 = DateUtil.today()
print(f"字符串创建: {d1}")
print(f"时间戳创建: {d2}")
print(f"今天: {d3}")
提示:Python的datetime模块本身就大量使用了类方法工厂模式,如datetime.fromtimestamp()、datetime.strptime()等,这是非常值得学习的实践。
六、实际应用场景
1. 数据解析器工厂
代码示例
import json
import csv
import io
class DataParser(ABC):
"""数据解析器基类"""
@abstractmethod
def parse(self, data):
pass
@abstractmethod
def serialize(self, obj):
pass
class JSONParser(DataParser):
def parse(self, data):
return json.loads(data)
def serialize(self, obj):
return json.dumps(obj, ensure_ascii=False, indent=2)
class CSVParser(DataParser):
def parse(self, data):
reader = csv.DictReader(io.StringIO(data))
return list(reader)
def serialize(self, obj):
if not obj:
return ""
output = io.StringIO()
writer = csv.DictWriter(output, fieldnames=obj[0].keys())
writer.writeheader()
writer.writerows(obj)
return output.getvalue()
class XMLParser(DataParser):
def parse(self, data):
# 简化的XML解析
print("解析XML数据")
return {}
def serialize(self, obj):
print("序列化为XML")
return "<root></root>"
# 解析器工厂
class ParserFactory:
"""文件格式解析器工厂"""
_parsers = {
"json": JSONParser,
"csv": CSVParser,
"xml": XMLParser
}
@classmethod
def get_parser(cls, file_format):
parser_cls = cls._parsers.get(file_format.lower())
if not parser_cls:
raise ValueError(f"不支持的文件格式: {file_format}")
return parser_cls()
# 使用
parser = ParserFactory.get_parser("json")
data = '{"name": "Python", "version": "3.12"}'
result = parser.parse(data)
print(f"解析结果: {result}")
csv_parser = ParserFactory.get_parser("csv")
csv_data = "name,age\nPython,30\nJava,28"
result = csv_parser.parse(csv_data)
print(f"CSV解析: {result}")
2. 通知服务工厂
代码示例
class Notification:
"""通知基类"""
def send(self, recipient, message):
raise NotImplementedError
class EmailNotification(Notification):
def send(self, recipient, message):
print(f"[邮件] 发送至 {recipient}: {message}")
class SMSNotification(Notification):
def send(self, recipient, message):
print(f"[短信] 发送至 {recipient}: {message}")
class PushNotification(Notification):
def send(self, recipient, message):
print(f"[推送] 发送至 {recipient}: {message}")
class WechatNotification(Notification):
def send(self, recipient, message):
print(f"[微信] 发送至 {recipient}: {message}")
class NotificationFactory:
"""通知服务工厂"""
@staticmethod
def create_notification(notification_type):
factories = {
"email": EmailNotification,
"sms": SMSNotification,
"push": PushNotification,
"wechat": WechatNotification
}
cls = factories.get(notification_type)
if not cls:
raise ValueError(f"不支持的通知类型: {notification_type}")
return cls()
# 使用
def send_alert(user, message, channels):
"""向用户发送多渠道通知"""
for channel in channels:
notification = NotificationFactory.create_notification(channel)
notification.send(user["phone"], message)
# 发送通知
user = {"name": "张三", "phone": "13800138000"}
send_alert(user, "服务器CPU使用率超过90%!", ["email", "sms", "wechat"])
小贴士
在实际项目中,可以结合依赖注入框架(如 dependency-injector)来管理工厂,让对象的创建和注入更加灵活和可测试。
七、小结与练习题
核心要点总结
-
简单工厂:一个工厂类通过条件判断创建不同产品,适合产品少的场景
-
工厂方法:将创建逻辑延迟到子类,每个产品对应一个工厂,符合开闭原则
-
抽象工厂:创建产品族的工厂,适用于多个相关产品的场景
-
Python特色:利用函数、类方法、装饰器和注册表,工厂模式实现更加灵活
练习题
练习1
请使用工厂方法模式实现一个图形渲染器,支持渲染不同格式的图片(PNG、JPEG、GIF、SVG),并使用注册表模式让新格式可以轻松扩展。
练习2
设计一个外卖平台的订单工厂,支持创建不同类型的订单(普通订单、预约订单、团购订单、拼单订单),每种订单有不同的处理流程和计费规则。在实际场景中应用工厂模式。
常见问题
工厂模式和依赖注入有什么区别?
工厂模式关注对象的创建过程,依赖注入关注对象之间的依赖关系管理。工厂可以是依赖注入的一种实现方式。在实际项目中,两者经常结合使用:通过依赖注入容器管理工厂,再由工厂创建具体对象。
Python中需要抽象基类才能实现工厂模式吗?
不需要。Python的鸭子类型特性意味着只要对象有相同的方法就可以互换使用。但使用ABC(抽象基类)可以提供更好的代码可读性和IDE支持,在大型项目中推荐使用。
简单工厂违反了开闭原则吗?
是的。简单工厂在添加新产品时需要修改工厂类的条件判断代码。如果需要严格遵守开闭原则,应该使用工厂方法模式或注册表模式,这样新增产品时只需添加新类,不需要修改已有代码。
工厂模式会影响性能吗?
工厂模式本身只会增加一层函数调用,性能影响可以忽略不计。但如果工厂中使用了大量的字典查找、反射或动态导入,在性能极度敏感的场景下需要注意优化。一般情况下,工厂模式带来的可维护性收益远大于微小的性能损失。
本文涉及AI创作
内容由AI创作,请仔细甄别