第 91 课 - 常见设计模式在 Python 中的应用
学习目标
- 理解设计模式的核心价值:为什么要在代码中应用设计模式。
- 掌握 Python 中 单例模式、工厂方法模式 和 观察者模式 的典型实现与应用场景。
- 学会根据代码的具体需求,选择合适的设计模式来提升代码的可维护性、灵活性和可扩展性。
核心概念
设计模式是软件工程中针对常见问题总结出的、可重用的解决方案模板。它们不是可以直接复制的代码,而是指导我们如何组织代码结构的"最佳实践"。在 Python 中,由于其动态特性,一些模式的实现比静态语言(如 Java)更加简洁。
1. 单例模式
- 通俗解释:确保一个类只有一个实例,并提供一个全局访问点。就像公司的 CEO,无论你问公司里的哪个人,他们指的 CEO 都是同一个人。
- 使用场景:管理共享资源,如数据库连接池、全局配置管理器、日志记录器等。
2. 工厂方法模式
- 通俗解释:定义一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法让类的实例化延迟到子类。就像一个汽车工厂,客户只需要告诉工厂需要"轿车"还是"SUV",具体制造哪款车型由工厂内部决定。
- 使用场景:当你需要创建多种类型的对象,并且希望将对象的创建过程与使用过程解耦时。
3. 观察者模式
- 通俗解释:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。就像你订阅了一个新闻网站,每当有新新闻发布,你的邮箱就会收到通知。
- 使用场景:事件处理系统、用户界面与业务逻辑的分离、消息推送等。
代码示例
# 示例 1:单例模式(使用 __new__ 方法实现)
class DatabaseConnection:
"""数据库连接单例"""
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
# 第一次创建时,实例化对象
cls._instance = super().__new__(cls)
print("创建新的数据库连接实例")
else:
print("使用已有的数据库连接实例")
return cls._instance
def __init__(self, host="localhost", port=3306):
# 注意:init 会在每次获取实例时都被调用
self.host = host
self.port = port
self.connected = False
def connect(self):
if not self.connected:
print(f"连接到数据库 {self.host}:{self.port}...")
self.connected = True
# 测试单例
db1 = DatabaseConnection("127.0.0.1", 3306)
db2 = DatabaseConnection("192.168.1.1", 5432)
print(f"db1 和 db2 是同一个对象: {db1 is db2}") # 输出 True
db1.connect() # 连接
print(f"db2 的连接状态: {db2.connected}") # 输出 True, 因为 db1 和 db2 是同一个对象
# 示例 2:工厂方法模式
from abc import ABC, abstractmethod
# 产品抽象基类
class PaymentProcessor(ABC):
@abstractmethod
def process_payment(self, amount: float) -> str:
pass
# 具体产品类
class CreditCardProcessor(PaymentProcessor):
def process_payment(self, amount: float) -> str:
return f"使用信用卡支付了 {amount} 元。"
class AliPayProcessor(PaymentProcessor):
def process_payment(self, amount: float) -> str:
return f"使用支付宝支付了 {amount} 元。"
# 工厂抽象基类
class PaymentProcessorFactory(ABC):
@abstractmethod
def create_processor(self) -> PaymentProcessor:
pass
# 具体工厂类
class CreditCardFactory(PaymentProcessorFactory):
def create_processor(self) -> PaymentProcessor:
return CreditCardProcessor()
class AliPayFactory(PaymentProcessorFactory):
def create_processor(self) -> PaymentProcessor:
return AliPayProcessor()
# 客户端代码
def process_order(payment_type: str, amount: float):
"""客户端只需要关心'类型',不关心具体实现"""
if payment_type == "credit_card":
factory = CreditCardFactory()
elif payment_type == "alipay":
factory = AliPayFactory()
else:
raise ValueError("不支持的支付方式")
processor = factory.create_processor()
return processor.process_payment(amount)
# 测试
print(process_order("credit_card", 100.50))
print(process_order("alipay", 200))
# 示例 3:观察者模式(使用 Python 特性简化实现)
class EventEmitter:
"""事件发射器(被观察者/主题)"""
def __init__(self):
self._events = {}
def on(self, event_name: str, callback):
"""注册事件监听器(订阅)"""
if event_name not in self._events:
self._events[event_name] = []
self._events[event_name].append(callback)
def emit(self, event_name: str, *args, **kwargs):
"""触发事件(发布)"""
if event_name in self._events:
for callback in self._events[event_name]:
callback(*args, **kwargs)
# 观察者/监听器
def on_data_saved(data):
print(f"[日志记录器] 数据已保存: {data}")
def on_data_saved_notification(data):
print(f"[通知服务] 您的数据 {data} 已成功保存。")
# 使用示例
data_bus = EventEmitter()
# 注册观察者
data_bus.on('data_saved', on_data_saved)
data_bus.on('data_saved', on_data_saved_notification)
# 当事件发生时
data_bus.emit('data_saved', {"user": "Alice", "id": 123})
# 输出:
# [日志记录器] 数据已保存: {'user': 'Alice', 'id': 123}
# [通知服务] 您的数据 {'user': 'Alice', 'id': 123} 已成功保存。
实践练习
练习 1:基础实现 请实现一个简单的配置管理器单例。要求:
- 类名为
ConfigManager。 - 它从一个字典中加载配置。
- 确保无论在哪里实例化,它都持有相同的配置数据。
# 预期测试代码
config1 = ConfigManager({"db": "localhost", "port": 3306})
config2 = ConfigManager({}) # 不应该覆盖 config1 的配置
print(config1.get("db")) # 应输出 localhost
print(config1 is config2) # 应输出 True
练习 2:模式应用 使用工厂方法模式创建一个简单的形状工厂。
- 定义抽象产品类
Shape,它有一个方法area()。 - 创建具体产品类
Circle和Rectangle。 - 创建
ShapeFactory,根据传入的字符串("circle"或"rectangle")返回相应的形状对象。 - 客户端代码只需告诉工厂要什么形状,无需知道具体类名。
练习 3:综合挑战
基于示例 3 的 EventEmitter,实现一个简单的用户注册系统。
- 创建一个
UserService类,它有一个register(user_info)方法。 - 当用户注册成功(在
register方法末尾),通过EventEmitter触发"user_registered"事件。 - 创建两个监听器:
EmailNotifier: 收到事件后,打印 "发送欢迎邮件给 {email}"。ActivityLogger: 收到事件后,打印 "用户 {username} 已于 [当前时间] 注册"。
常见错误
- 滥用设计模式:不是所有问题都需要设计模式。简单的代码用复杂模式实现,只会增加不必要的复杂性。请先思考当前代码是否真的遇到了设计模式所针对的问题。
- 单例模式的过度使用:过度使用单例会导致代码耦合度变高,难以测试(因为它可能持有状态)。考虑依赖注入是否是更好的选择。
- 忽略 Python 的动态特性:在 Python 中,有时一个简单的函数或一个类装饰器就能达到类似"工厂"或"观察者"的效果,不必严格遵循 GoF(《设计模式》)中的类结构。我们的目标是解决问题,而非套用模式。
小结
- 设计模式是解决常见软件设计问题的、经过验证的最佳实践,能显著提升代码质量。
- 单例模式 用于控制实例数量,管理共享资源,Python 中常用
__new__或元类实现。 - 工厂方法模式 将对象的创建与使用分离,提供了优秀的扩展性,符合开闭原则。
- 观察者模式 实现了对象间的松耦合通信,是事件驱动架构的基石。
- 关键要点:理解模式背后的设计原则(如单一职责、开闭原则)比记住实现代码更重要。在 Python 中,应灵活运用其语言特性,以最 Pythonic 的方式解决问题。
练习编辑器
python
Loading...