当前位置: 首页 > article >正文

Python 监听模式(Observer Pattern)

1. 监听模式技术方案

监听模式(Observer Pattern)是一种行为设计模式,允许对象(称为“观察者”或“监听者”)在另一个对象(称为“被观察者”或“主题”)的状态发生变化时接收通知。这种模式的核心思想是解耦,使得被观察者和观察者之间没有直接的依赖关系,从而提高系统的灵活性和可维护性。

在监听模式中,观察者可以动态地注册和注销到被观察者。当被观察者的状态发生变化时,它会通知所有注册的观察者,观察者可以根据需要执行相应的操作。这种模式特别适用于需要处理异步事件的场景,例如用户界面事件、数据变化通知等。

1.1 主要组成部分

  1. 被观察者(Subject)

    • 被观察者是监听模式的核心,负责管理观察者的注册、注销和通知。它维护一个观察者列表,当其状态发生变化时,它会遍历这个列表并通知所有注册的观察者。
    • 被观察者通常提供以下方法:
      • 注册观察者:允许观察者注册到被观察者,以便接收通知。
      • 注销观察者:允许观察者从被观察者中注销,以停止接收通知。
      • 通知观察者:在状态变化时,遍历观察者列表并调用每个观察者的更新方法,传递相关的事件信息。
  2. 观察者(Observer)

    • 观察者是实现特定接口的对象,用于接收来自被观察者的通知。观察者在被观察者的状态变化时执行相应的操作。
    • 观察者通常定义一个更新方法,当被观察者发出通知时,该方法会被调用。观察者可以根据接收到的事件信息执行相应的操作。
  3. 事件(Event)

    • 事件是被观察者状态变化时传递给观察者的信息。事件通常封装了与状态变化相关的数据,观察者可以根据这些数据做出相应的反应。
    • 事件对象通常包含以下信息:
      • 事件类型:指示发生的事件类型,例如温度变化、状态变化等。
      • 事件数据:与事件相关的具体数据,例如新的温度值、状态信息等。

1.2 工作流程

监听模式的工作流程如下:

  1. 注册观察者:观察者通过调用被观察者的注册方法,将自己注册到被观察者中。
  2. 状态变化:被观察者的状态发生变化,例如数据更新、用户操作等。
  3. 通知观察者:被观察者调用通知方法,遍历观察者列表,并调用每个观察者的更新方法,传递事件信息。
  4. 处理事件:观察者接收到通知后,根据事件信息执行相应的操作。

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()
  1. TemperatureEvent 类

    • 该类用于封装温度事件,包含当前温度信息。
  2. WaterHeater 类

    • 作为被观察者,负责管理温度变化和通知监听器。它有一个 heat_water 方法,每秒将温度上升 10 度,并在每次温度变化时通知所有注册的监听器。
  3. BathListener 类

    • 作为观察者,处理温度达到 40 度的事件。它使用 notified 状态标志确保只在温度达到 40 度时通知一次。
  4. BoilListener 类

    • 另一个观察者,处理温度达到 100 度的事件,同样使用 notified 状态标志确保只通知一次。
  5. 使用示例

    • 创建热水器和两个监听器,注册监听器,并启动热水器加热水。随着温度的变化,监听器会根据设定的条件做出相应的反应。

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!")
  1. EmailEvent 类

    • 该类用于封装电子邮件事件,包含发件人和邮件主题信息。
  2. Mailbox 类

    • 作为被观察者,负责接收新邮件并通知监听器。它有一个 receive_email 方法,用于模拟接收新邮件,并在每次接收时通知所有注册的监听器。
  3. EmailNotificationListener 类

    • 作为观察者,处理新邮件到达事件。它在 update 方法中打印新邮件的发件人和主题信息。
  4. SpamFilterListener 类

    • 另一个观察者,处理新邮件到达事件。它检查邮件主题是否包含“spam”字样,并根据结果打印相应的消息。
  5. 使用示例

    • 创建邮箱和两个监听器,注册监听器,并模拟接收新邮件。每当新邮件到达时,两个监听器都会根据事件信息做出相应的反应。

http://www.kler.cn/a/593374.html

相关文章:

  • 【LeetCode】622、设计循环队列
  • 软考程序员考试知识点汇总
  • Java爬虫抓取B站视频信息
  • C#实现的一个简单的软件保护方案
  • Unity框架集合(持续更新)
  • 【最后203篇系列】016 Q201架构思考
  • 移动端高效3D场景重建技术突破:从2D图像到语义化空间理解的范式迁移
  • 私有云大数据部署:从开发到生产(Docker、K8s、HDFS/Flink on K8s)
  • 数学——A. K-divisible Sum + D. Exam in MAC
  • Redis项目:秒杀业务
  • mysql与redis的日志策略
  • 汇能感知高品质的多光谱相机VSC02UA
  • 如何使用MySQL快速定位慢SQL问题?企业级开发中常见业务场景中实际发生的例子,涉及分页查询问题。(二)
  • HTML中required与aria required区别
  • 数字孪生的建模师blender和maya你更喜欢用哪个?
  • 【初学者】算法的学科体系是怎样的?请分章节介绍一下?
  • 计算机四级 - 数据库原理 - 第8章「分布式、对象-关系、NOSQL数据库」
  • 【QT:QT事件】
  • RabbitMQ可靠性进制
  • 汽车安全确认等级-中国等保