发布 - 订阅模式 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(事件总线)是发布 - 订阅模式的经典应用场景,其他适用场景包括:- 前端事件系统(如按钮点击、鼠标悬浮事件);
- 工业控制软件(如 KUKA 控制柜的状态变更通知、传感器数据上报);
- 微服务通信(跨服务事件通知);
- 日志系统(多模块订阅日志事件,分别输出到文件 / 控制台 / 数据库);
- 插件化系统(插件间通过 EventBus 通信,无需硬耦合)。
五、补充:代码原版本的小问题(新手避坑)
原代码存在 2 个语法 / 逻辑问题,已在完整代码中修复:
emit方法中使用listener(*args),但参数只有data,应为listener(data);on方法中直接self.listeners[event].append,未处理「事件不存在」的情况,会报 KeyError;off方法中直接listeners.remove,未判断监听器是否存在,会报 ValueError。
总结
- 核心模式:发布 - 订阅模式(Pub/Sub)(行为型模式),是观察者模式的解耦扩展版;
- 核心逻辑:通过
EventBus作为中间层,「订阅(on)- 发布(emit)- 取消订阅(off)」实现事件的解耦通知; - 核心价值:解耦事件发布者和订阅者,新增 / 移除订阅者无需修改发布者代码,提升系统灵活性和可维护性;
- 典型场景:事件总线、跨模块通信、插件化系统、工业控制软件的状态通知。
本文来自博客园,作者:limingqi,转载请注明原文链接:https://www.cnblogs.com/limingqi/p/19734311
浙公网安备 33010602011771号