发布-订阅(Publish–Subscribe) vs 观察者模式(Observer Pattern)

在日常开发中,我们经常会听到"发布-订阅模式"和"观察者模式"这两个概念。

它们看起来非常相似,甚至很多人以为是同一个东西。

但其实------它们思想相近,结构不同,耦合度差别巨大

一、两者到底有什么区别?

我们先用一句话总结👇

观察者模式: 被观察者直接通知观察者。
发布-订阅模式: 发布者和订阅者通过中间事件中心通信。

来看下对比表:

项目 观察者模式(Observer Pattern) 发布-订阅模式(Publish--Subscribe Pattern)
核心思想 对象直接观察另一个对象的状态变化 借助第三方"事件通道"(事件总线 / 消息代理)
参与者 被观察者(Subject)+ 观察者(Observer) 发布者(Publisher)+ 订阅者(Subscriber)+ 中间件(Broker/EventBus)
通信方式 观察者直接注册到被观察者上 双方通过事件中心间接通信
耦合度 高(Subject 必须知道 Observers) 低(Publisher 不知道 Subscriber)
典型场景 Vue2 响应式系统、数据绑定 Node.js EventEmitter、DOM事件系统、微前端通信

二、观察者模式(Observer Pattern)

被观察者直接持有观察者引用,状态变化时主动通知。

思想图

flowchart TD A[Subject] --> B[Observer 1] A --> C[Observer 2] A --> D[Observer 3]

代码示例

javascript 复制代码
// 被观察者
class Subject {
  constructor() {
    this.observers = [];
  }

  // 添加观察者
  attach(observer) {
    this.observers.push(observer);
  }

  // 移除观察者
  detach(observer) {
    this.observers = this.observers.filter(o => o !== observer);
  }

  // 通知所有观察者
  notify(data) {
    this.observers.forEach(observer => observer.update(data));
  }
}

// 观察者
class Observer {
  constructor(name) {
    this.name = name;
  }

  update(data) {
    console.log(`${this.name} 收到通知:`, data);
  }
}

// 使用
const subject = new Subject();
const obs1 = new Observer('观察者A');
const obs2 = new Observer('观察者B');

subject.attach(obs1);
subject.attach(obs2);

subject.notify('主题发生变化');
// 输出:
// 观察者A 收到通知: 主题发生变化
// 观察者B 收到通知: 主题发生变化

特点总结

  • 观察者必须主动注册到被观察者;
  • 被观察者维护观察者列表;
  • 二者直接绑定,耦合较高;
  • 常用于对象间状态同步。

三、发布-订阅模式(Publish--Subscribe Pattern)

借助一个"事件总线(EventBus)"作为中介,发布者和订阅者完全解耦。

🧠 思想图

css 复制代码
flowchart TD
  subgraph Bus[事件总线]
    X[EventBus]
  end
  A[Publisher] --> X
  X --> B[Subscriber 1]
  X --> C[Subscriber 2]
  X --> D[Subscriber 3]

代码示例

kotlin 复制代码
class EventBus {
  constructor() {
    this.events = {};
  }

  // 订阅事件
  subscribe(event, callback) {
    if (!this.events[event]) this.events[event] = [];
    this.events[event].push(callback);
  }

  // 取消订阅
  unsubscribe(event, callback) {
    if (!this.events[event]) return;
    this.events[event] = this.events[event].filter(cb => cb !== callback);
  }

  // 发布事件
  publish(event, data) {
    if (!this.events[event]) return;
    this.events[event].forEach(cb => cb(data));
  }
}

// 使用
const bus = new EventBus();

bus.subscribe('orderCreated', (data) => {
  console.log('📦 订单模块收到新订单:', data);
});

bus.subscribe('orderCreated', (data) => {
  console.log('📊 数据模块统计新订单:', data);
});

// 发布事件
bus.publish('orderCreated', { id: 101, item: '卡丁车' });

✅ 特点总结

  • 发布者与订阅者完全解耦;
  • 可以存在多个独立事件通道;
  • 更适合模块化系统或跨组件通信;
  • 可扩展性更强。

四、浏览器中的 EventTarget:内置的发布订阅系统

浏览器其实自带一个发布订阅机制------EventTarget

所有常见的事件系统(windowdocumentHTMLElement)都继承自它。

javascript 复制代码
const btn = document.querySelector('button');

// 订阅
btn.addEventListener('click', (e) => {
  console.log('按钮被点击了', e);
});

// 发布
btn.dispatchEvent(new Event('click'));

五、总结

模式 通信关系 是否解耦 常见场景
观察者模式 被观察者 ↔ 观察者 ❌ 否 Vue2 响应式系统
发布-订阅模式 发布者 → 事件中心 → 订阅者 ✅ 是 Node.js EventEmitter、浏览器事件、消息系统
EventTarget 浏览器内置的发布订阅实现 ✅ 是 DOM 事件系统
相关推荐
老华带你飞9 分钟前
海产品销售系统|海鲜商城购物|基于SprinBoot+vue的海鲜商城系统(源码+数据库+文档)
java·前端·数据库·vue.js·论文·毕设·海鲜商城购物系统
x***B41119 分钟前
React安全编程实践
前端·安全·react.js
D***t13135 分钟前
前端微服务案例
前端
哀木1 小时前
诶,这么好用的 mock 你怎么不早说
前端
Lear1 小时前
UniApp PDF文件下载与预览功能完整实现指南
前端
Heo1 小时前
关于XSS和CSRF,面试官更喜欢这样的回答!
前端·javascript·面试
7***A4431 小时前
Vue自然语言处理应用
前端·vue.js·自然语言处理
高阳言编程2 小时前
vue2 + node + express + MySQL 5.7 的购物系统
前端
y***54882 小时前
React依赖
前端·react.js·前端框架
2***B4492 小时前
React测试
前端·react.js·前端框架