js 观察者模式和发布订阅模式详解

一、核心定义与角色差异

  1. ‌观察者模式(Observer Pattern) ‌

    • ‌核心角色‌:

      • ‌Subject ‌(被观察者):直接维护 Observer 列表,提供注册/注销接口,状态变更时主动遍历列表通知所有观察者。
      • ‌Observer ‌(观察者):定义统一回调方法(如 update()),接收 Subject 的变更通知。
    • ‌通信方式‌:

      • 直接绑定,Subject 与 Observer 强耦合,无中间层。
      js 复制代码
      class Subject {
        constructor() { this.observers = []; }
        add(observer) { this.observers.push(observer); }
        notify(data) { this.observers.forEach(obs => obs.update(data)); }
      }
      class Observer {
        update(data) { console.log(`收到更新:${data}`); }
      }
      
      //使用
      class MyObserver extends Observer{
        constructor(){
          super()
        }
        // 重写父类方法
        notify(data){
          console.log(`MyObserver数据更新:${data}`)
        }
      }
      const sj = new Subject();
      const mo = new MyObserver();
      sj.add(mo);
      sj.update("更新完成");  // MyObserver数据更新:更新完成
  2. ‌发布订阅模式(Pub-Sub Pattern) ‌

    • ‌核心角色‌:

      • ‌Publisher ‌(发布者):仅负责向中间层(Broker)发布事件,不感知订阅者。
      • ‌Subscriber‌(订阅者):通过中间层订阅特定主题(Topic),执行自定义回调。
      • ‌Broker‌(消息中心):管理主题与订阅关系,负责消息路由和分发。
    • ‌通信方式‌:

      • 通过中间层解耦,Publisher 和 Subscriber 互不感知,基于主题或事件类型通信。
      js 复制代码
      class EventBus {
        constructor() { this.topics = {}; }
        subscribe(topic, callback) {
          this.topics[topic] = this.topics[topic] || [];
          this.topics[topic].push(callback);
        }
        publish(topic, data) {
          this.topics[topic]?.forEach(cb => cb(data));
        }
      }
      
      // 使用
      const ev = new EventBus();
      ev.subscribe("myName", data =>{
        console.log(data)
      });
      ev.publish("myName", "触发myName成功");

二、关键区别对比

‌维度‌ ‌观察者模式‌ ‌发布订阅模式‌ 来源
‌耦合度‌ 高耦合(Subject 直接管理 Observer) 低耦合(通过 Broker 解耦) 13
‌通信方向‌ 单向:Subject → Observer 多向:支持多发布者、多订阅者 34
‌中间层‌ 无 必须存在 Broker 或事件中心 14
‌事件处理粒度‌ 所有 Observer 执行相同逻辑(如 update()) 基于不同主题执行差异化回调 58
‌扩展性‌ 动态增删 Observer 较灵活 支持动态订阅/取消、跨模块通信 34
‌异步支持‌ 通常同步执行 天然支持异步(如消息队列延迟分发) 58

三、适用场景

  1. ‌观察者模式适用场景‌

    • ‌UI 组件状态同步‌:如 Vue 的响应式数据依赖追踪。
    • ‌原生事件监听‌:如 DOM 事件(点击、滚动)的绑定与触发。
    • ‌局部状态管理‌:父子组件或同一模块内的直接通信。
  2. ‌发布订阅模式适用场景‌

    • ‌跨组件/模块通信 ‌:如 Vue 的 EventBus、React 的全局事件总线。
    • ‌分布式系统消息传递‌:微服务间通过消息队列解耦通信14。
    • ‌动态订阅场景‌:如实时聊天室、新闻推送(用户按兴趣订阅)。

四、实现选择建议

  1. ‌优先观察者模式的情况‌

    • 需要简单的一对多通知逻辑,且通信双方关系明确。
    • 高频同步操作(如游戏引擎的状态更新)。
  2. ‌优先发布订阅模式的情况‌

    • 需要解耦多对多通信、支持动态订阅/取消。
    • 异步场景(如 HTTP 请求结果分发)或跨项目模块通信。

五、实战示例

  1. ‌观察者模式:DOM 事件监听‌

    js 复制代码
    const button = document.querySelector('button');
    button.addEventListener('click', (e) => {
      console.log('按钮被点击!'); // Observer 逻辑
    });
  2. ‌发布订阅模式:全局事件总线‌

    js 复制代码
    const eventBus = new EventBus();
    // 订阅
    eventBus.subscribe('message', (data) => {
      console.log(`收到消息:${data}`);
    });
    // 发布
    eventBus.publish('message', 'Hello Pub-Sub!');

六、总结

  • ‌观察者模式 ‌:适用于 ‌强绑定、直接通信‌ 的场景,实现简单但扩展性受限。
  • ‌发布订阅模式 ‌:适用于 ‌解耦、动态、异步‌ 的复杂场景,通过中间层提升灵活性。
  • ‌选择依据 ‌:根据 ‌耦合需求、通信复杂度、性能要求‌ 综合权衡,避免滥用单一模式。
相关推荐
清晨Coding13 小时前
删了 Cookie 还能自动登录?Google系网站登录态自动恢复的原理分析
javascript
kyriewen13 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
程序员大阳14 小时前
HTML+CSS+JS对话式实战(9)-项目总结与问题分析
javascript·css·html·实战·项目总结
Hilaku15 小时前
MCP 协议会成为前端 AI 工程的标准接口吗?
前端·javascript·程序员
RobinDevNotes18 小时前
从 Electron 到 GPUI,9 个跨平台桌面方案该怎么选
前端·javascript·rust·electron·openresty·前端开发工具
程序员大阳19 小时前
HTML+CSS+JS对话式实战(6)-学生端查看活动功能开发
javascript·css·html·模块·查看学生
子兮曰2 天前
Chrome 权限元素实战:144 与 151 的渐进增强写法
前端·javascript·chrome
强尼银手2 天前
我花一天做了个格斗游戏原型,判定全对,然后被「美术」劝退了
前端·javascript
总要学点什么2 天前
第98篇:三维特效-材质线效果——给一条线换十种材质,虚线的图案只填了一半
开发语言·前端·javascript·ecmascript·gis·webgl·材质
OpsEye2 天前
混合云 AI 架构,如何统一做敏感内容拦截?
javascript·ai编程