Python 监听模式(Observer Pattern)
1. 监听模式技术方案
监听模式(Observer Pattern)是一种行为设计模式,允许对象(称为“观察者”或“监听者”)在另一个对象(称为“被观察者”或“主题”)的状态发生变化时接收通知。这种模式的核心思想是解耦,使得被观察者和观察者之间没有直接的依赖关系,从而提高系统的灵活性和可维护性。
在监听模式中,观察者可以动态地注册和注销到被观察者。当被观察者的状态发生变化时,它会通知所有注册的观察者,观察者可以根据需要执行相应的操作。这种模式特别适用于需要处理异步事件的场景,例如用户界面事件、数据变化通知等。
1.1 主要组成部分
-
被观察者(Subject):
- 被观察者是监听模式的核心,负责管理观察者的注册、注销和通知。它维护一个观察者列表,当其状态发生变化时,它会遍历这个列表并通知所有注册的观察者。
- 被观察者通常提供以下方法:
- 注册观察者:允许观察者注册到被观察者,以便接收通知。
- 注销观察者:允许观察者从被观察者中注销,以停止接收通知。
- 通知观察者:在状态变化时,遍历观察者列表并调用每个观察者的更新方法,传递相关的事件信息。
-
观察者(Observer):
- 观察者是实现特定接口的对象,用于接收来自被观察者的通知。观察者在被观察者的状态变化时执行相应的操作。
- 观察者通常定义一个更新方法,当被观察者发出通知时,该方法会被调用。观察者可以根据接收到的事件信息执行相应的操作。
-
事件(Event):
- 事件是被观察者状态变化时传递给观察者的信息。事件通常封装了与状态变化相关的数据,观察者可以根据这些数据做出相应的反应。
- 事件对象通常包含以下信息:
- 事件类型:指示发生的事件类型,例如温度变化、状态变化等。
- 事件数据:与事件相关的具体数据,例如新的温度值、状态信息等。
1.2 工作流程
监听模式的工作流程如下:
- 注册观察者:观察者通过调用被观察者的注册方法,将自己注册到被观察者中。
- 状态变化:被观察者的状态发生变化,例如数据更新、用户操作等。
- 通知观察者:被观察者调用通知方法,遍历观察者列表,并调用每个观察者的更新方法,传递事件信息。
- 处理事件:观察者接收到通知后,根据事件信息执行相应的操作。
1.3 优点
- 解耦:被观察者和观察者之间没有直接的依赖关系,便于系统的扩展和维护。
- 动态性:观察者可以在运行时注册和注销,灵活应对变化。
- 多对多关系:一个被观察者可以有多个观察者,一个观察者也可以观察多个被观察者,适应复杂的应用场景。
1.4 缺点
- 内存泄漏:如果观察者没有正确注销,可能导致内存泄漏。
- 性能问题:在观察者数量较多时,通知所有观察者可能会影响性能。
1.5 应用场景
监听模式广泛应用于各种场景,尤其是在需要事件驱动的系统中。以下是一些常见的应用场景:
- 图形用户界面(GUI):在 GUI 应用程序中,用户的操作(如按钮点击、文本输入等)可以触发事件,监听器可以响应这些事件并执行相应的操作。
- 实时数据监控:在数据监控系统中,数据源的变化可以触发通知,监听器可以根据变化更新显示或执行其他操作。
- 游戏开发:在游戏中,游戏对象的状态变化(如生命值变化、得分变化等)可以通过监听模式通知其他对象。
- 网络编程:在网络应用中,连接状态的变化(如连接建立、断开等)可以通过监听模式进行处理。
2. 例子1:热水器温度监测系统
import time
class TemperatureEvent:
"""温度事件类,包含当前温度"""
def __init__(self, temperature):
self.temperature = temperature # 当前温度
class WaterHeater:
"""热水器,负责温度变化并通知监听器"""
def __init__(self):
self.listeners = [] # 存储注册的监听器
self.temperature = 20 # 初始温度
def register_listener(self, listener):
self.listeners.append(listener) # 注册监听器
def notify_listeners(self, event):
for listener in self.listeners:
listener.update(event) # 通知所有监听器
def heat_water(self):
"""模拟热水器加热水,每秒温度上升 10 度"""
while self.temperature < 100:
time.sleep(1) # 每秒加热
self.temperature += 10 # 温度上升 10 度
print(f"Current Temperature: {self.temperature}°C")
event = TemperatureEvent(self.temperature)
self.notify_listeners(event) # 通知监听器
class BathListener:
"""洗澡监听器,处理温度达到 40 度的事件"""
def __init__(self):
self.notified = False # 状态标志,确保只通知一次
def update(self, event):
if event.temperature >= 40 and not self.notified:
print("BathListener: The water is warm enough for a bath!")
self.notified = True # 设置状态标志为 True,表示已通知
class BoilListener:
"""烧开监听器,处理温度达到 100 度的事件"""
def __init__(self):
self.notified = False # 状态标志,确保只通知一次
def update(self, event):
if event.temperature >= 100 and not self.notified:
print("BoilListener: The water has boiled!")
self.notified = True # 设置状态标志为 True,表示已通知
# 创建热水器和监听器
water_heater = WaterHeater()
bath_listener = BathListener()
boil_listener = BoilListener()
# 注册监听器
water_heater.register_listener(bath_listener)
water_heater.register_listener(boil_listener)
# 启动热水器加热水
water_heater.heat_water()
-
TemperatureEvent 类:
- 该类用于封装温度事件,包含当前温度信息。
-
WaterHeater 类:
- 作为被观察者,负责管理温度变化和通知监听器。它有一个
heat_water
方法,每秒将温度上升 10 度,并在每次温度变化时通知所有注册的监听器。
- 作为被观察者,负责管理温度变化和通知监听器。它有一个
-
BathListener 类:
- 作为观察者,处理温度达到 40 度的事件。它使用
notified
状态标志确保只在温度达到 40 度时通知一次。
- 作为观察者,处理温度达到 40 度的事件。它使用
-
BoilListener 类:
- 另一个观察者,处理温度达到 100 度的事件,同样使用
notified
状态标志确保只通知一次。
- 另一个观察者,处理温度达到 100 度的事件,同样使用
-
使用示例:
- 创建热水器和两个监听器,注册监听器,并启动热水器加热水。随着温度的变化,监听器会根据设定的条件做出相应的反应。
3. 例子2:电子邮件通知系统
import time
class EmailEvent:
"""电子邮件事件类,包含邮件信息"""
def __init__(self, sender, subject):
self.sender = sender # 发件人
self.subject = subject # 邮件主题
class Mailbox:
"""邮箱,负责接收新邮件并通知监听器"""
def __init__(self):
self.listeners = [] # 存储注册的监听器
def register_listener(self, listener):
self.listeners.append(listener) # 注册监听器
def notify_listeners(self, event):
for listener in self.listeners:
listener.update(event) # 通知所有监听器
def receive_email(self, sender, subject):
"""模拟接收新邮件"""
print(f"New email received from {sender}: {subject}")
event = EmailEvent(sender, subject)
self.notify_listeners(event) # 通知监听器
class EmailNotificationListener:
"""电子邮件通知监听器,处理新邮件到达事件"""
def update(self, event):
print(f"EmailNotificationListener: You have a new email from {event.sender} with subject: '{event.subject}'")
class SpamFilterListener:
"""垃圾邮件过滤监听器,处理新邮件到达事件"""
def update(self, event):
if "spam" in event.subject.lower():
print(f"SpamFilterListener: This email from {event.sender} is marked as spam: '{event.subject}'")
else:
print(f"SpamFilterListener: This email from {event.sender} is not spam.")
# 创建邮箱和监听器
mailbox = Mailbox()
email_listener = EmailNotificationListener()
spam_listener = SpamFilterListener()
# 注册监听器
mailbox.register_listener(email_listener)
mailbox.register_listener(spam_listener)
# 模拟接收新邮件
mailbox.receive_email("alice@example.com", "Meeting Reminder")
time.sleep(1)
mailbox.receive_email("bob@example.com", "Project Update")
time.sleep(1)
mailbox.receive_email("charlie@example.com", "Spam: Win a Million Dollars!")
-
EmailEvent 类:
- 该类用于封装电子邮件事件,包含发件人和邮件主题信息。
-
Mailbox 类:
- 作为被观察者,负责接收新邮件并通知监听器。它有一个
receive_email
方法,用于模拟接收新邮件,并在每次接收时通知所有注册的监听器。
- 作为被观察者,负责接收新邮件并通知监听器。它有一个
-
EmailNotificationListener 类:
- 作为观察者,处理新邮件到达事件。它在
update
方法中打印新邮件的发件人和主题信息。
- 作为观察者,处理新邮件到达事件。它在
-
SpamFilterListener 类:
- 另一个观察者,处理新邮件到达事件。它检查邮件主题是否包含“spam”字样,并根据结果打印相应的消息。
-
使用示例:
- 创建邮箱和两个监听器,注册监听器,并模拟接收新邮件。每当新邮件到达时,两个监听器都会根据事件信息做出相应的反应。