发布 - 订阅模式 python

一、核心模式:发布 - 订阅模式(Publish-Subscribe Pattern)

这段代码是发布 - 订阅模式(Pub/Sub)(也常被称为「观察者模式」的变种)的典型实现,属于设计模式中「行为型模式」,核心是解耦「事件发布者」和「事件订阅者」—— 发布者(EventBus)只负责触发事件,无需知道谁在监听;订阅者只需注册感兴趣的事件,无需知道事件由谁发布。

1. 发布 - 订阅模式的核心定义

  • 发布者(Publisher):触发事件的一方(这里的 EventBus 是核心发布者,通过 emit 方法发布事件);
  • 订阅者(Subscriber):监听事件的一方(通过 on 注册的 EventListener 是订阅者);
  • 事件通道(Channel):通过事件名称(event 字符串)区分不同事件,作为发布者和订阅者的中间桥梁;
  • 解耦核心:发布者和订阅者互不感知,仅通过 EventBus 交互,新增 / 移除订阅者无需修改发布者代码。

二、代码逐行拆解(发布 - 订阅模式的关键特征)

先补充完整上下文(修复语法问题 + 补全依赖),再分析核心逻辑:
from typing import Dict, List, Any, Callable
from pydantic import BaseModel, PrivateAttr

# 补充基础类型和基类(基于你之前的代码上下文)
EventListener = Callable[..., Any]  # 监听器是任意可调用对象(函数/方法)
class Plugin(BaseModel):
    name: str = "event_bus"

class EventBus(Plugin):
    # 1. 事件注册表:key=事件名,value=该事件的监听器列表
    listeners: Dict[str, List[EventListener]] = PrivateAttr(default_factory=dict)
    
    def emit(self, event: str, data: Any) -> bool:
        """发布/触发事件(核心发布逻辑)"""
        # 获取该事件的所有监听器(无则返回空列表,避免报错)
        listeners = self.listeners.get(event, [])
        if not listeners:
            return False  # 无监听器,触发失败
        # 通知所有订阅者:执行监听器并传入事件数据
        for listener in listeners:
            listener(data)  # 修复原代码的args错误,应为data
        return True
    
    def on(self, event: str, listener: EventListener) -> 'EventBus':
        """订阅事件(注册监听器)"""
        # 事件不存在则初始化空列表
        if event not in self.listeners:
            self.listeners[event] = []
        # 将监听器添加到对应事件的列表中
        self.listeners[event].append(listener)
        return self  # 链式调用(如bus.on("click", func).on("hover", func))
    
    def off(self, event: str, listener: EventListener) -> 'EventBus':
        """取消订阅(移除监听器)"""
        listeners = self.listeners.get(event, [])
        if listener in listeners:
            listeners.remove(listener)
        return self
关键设计点(发布 - 订阅模式的核心):
 
代码片段模式特征作用
listeners: Dict[str, List[EventListener]] 事件注册表 核心数据结构,存储「事件名 - 监听器列表」的映射,是发布和订阅的桥梁;
emit(event, data) 发布事件 遍历指定事件的所有监听器,执行并传入事件数据,完成「发布」动作;
on(event, listener) 订阅事件 将监听器注册到指定事件的列表中,完成「订阅」动作;
off(event, listener) 取消订阅 从事件列表中移除指定监听器,停止接收该事件的通知;
 

三、与观察者模式的区别(避免混淆)

很多人会把发布 - 订阅和观察者模式混为一谈,核心区别如下:
 
维度观察者模式发布 - 订阅模式
耦合度 观察者直接依赖主题(Subject),耦合较高 发布者和订阅者通过「事件总线」解耦,无直接依赖
中间层 无中间层,主题直接通知观察者 有 EventBus 作为中间层,事件通过「事件名」路由
灵活性 新增观察者需修改主题(或主题提供注册接口) 新增订阅者无需修改发布者,仅需注册到 EventBus
 
你的代码属于发布 - 订阅模式:EventBus 是独立的中间层,事件通过名称区分,发布者(调用 emit)和订阅者(调用 on)完全解耦。

四、该模式的适用场景(为什么 EventBus 要用这个模式)

EventBus(事件总线)是发布 - 订阅模式的经典应用场景,其他适用场景包括:
  1. 前端事件系统(如按钮点击、鼠标悬浮事件);
  2. 工业控制软件(如 KUKA 控制柜的状态变更通知、传感器数据上报);
  3. 微服务通信(跨服务事件通知);
  4. 日志系统(多模块订阅日志事件,分别输出到文件 / 控制台 / 数据库);
  5. 插件化系统(插件间通过 EventBus 通信,无需硬耦合)。

五、补充:代码原版本的小问题(新手避坑)

原代码存在 2 个语法 / 逻辑问题,已在完整代码中修复:
  1. emit 方法中使用 listener(*args),但参数只有 data,应为 listener(data)
  2. on 方法中直接 self.listeners[event].append,未处理「事件不存在」的情况,会报 KeyError;
  3. off 方法中直接 listeners.remove,未判断监听器是否存在,会报 ValueError。

总结

  1. 核心模式:发布 - 订阅模式(Pub/Sub)(行为型模式),是观察者模式的解耦扩展版;
  2. 核心逻辑:通过 EventBus 作为中间层,「订阅(on)- 发布(emit)- 取消订阅(off)」实现事件的解耦通知;
  3. 核心价值:解耦事件发布者和订阅者,新增 / 移除订阅者无需修改发布者代码,提升系统灵活性和可维护性;
  4. 典型场景:事件总线、跨模块通信、插件化系统、工业控制软件的状态通知。

posted on 2026-03-18 15:25  limingqi  阅读(37)  评论(0)    收藏  举报

导航