发布订阅和观察者模式到底有啥区别?为何总让人傻傻分不清?

发布订阅模式 vs 观察者模式:为何总让人傻傻分不清?

当你在技术文档里频繁看到「发布订阅模式」和「观察者模式」时,是否总觉得两者像一对孪生兄弟?程序员论坛里常年有人发问:"这俩设计模式到底有什么区别?" 更令人困惑的是,很多技术文章将它们混为一谈。本文将通过一个水果资讯公司的真实场景,为你划清这两种模式的技术边界。

一、为什么需要这两种模式?

假设某水果资讯平台需要实时推送芒果价格波动、榴莲上市信息等数据。当价格变动时,需要同时通知:

  • APP推送服务
  • 短信通知系统
  • 数据分析后台

如果采用传统轮询方式,不仅会造成资源浪费,更难以应对突发的高频数据更新。这正是观察者模式与发布订阅模式要解决的核心问题。

二、观察者模式:没有中间商赚差价

1. 核心机制

观察者模式就像水果市场的实时报价牌:

  1. 被观察者(Subject)维护观察者列表
  2. 当状态变化时直接调用观察者的update()方法

2. 代码示例

class FruitPriceSubject {
  constructor() {
    this.observers = [];
    this.price = 0;
  }
  
  notify() {
    this.observers.forEach(observer => observer.update(this.price));
  }
}

class AppObserver {
  update(price) {
    console.log(`APP收到新价格:${price}`);
  }
}

3. 关键特点

  • 直接通信:被观察者持有观察者引用
  • 强耦合:观察者必须实现统一接口
  • 实时性高:状态变更立即触发通知

三、发布订阅模式:消息中间件的崛起

1. 核心机制

发布订阅模式更像水果批发市场的信息中心:

  1. 发布者向事件中心发布消息
  2. 订阅者通过事件中心注册回调
  3. 事件中心负责路由和转发

2. 代码示例

class EventBus {
  constructor() {
    this.events = {};
  }
  
  subscribe(event, callback) {
    if(!this.events[event]) this.events[event] = [];
    this.events[event].push(callback);
  }
  
  publish(event, data) {
    this.events[event]?.forEach(cb => cb(data));
  }
}

const bus = new EventBus();
bus.subscribe('mango_price', price => {
  console.log(`短信系统收到芒果价格:${price}`);
});

3. 关键特点

  • 解耦设计:发布者与订阅者互不知晓
  • 灵活路由:支持多种消息过滤机制
  • 可扩展性强:适合分布式系统

四、本质区别对照表

对比维度观察者模式发布订阅模式
通信方式直接方法调用通过中间件转发
耦合程度紧耦合(需维护引用)松耦合(仅依赖事件类型)
扩展成本修改主体代码动态添加订阅者
典型应用GUI事件处理微服务通信

五、如何选择?

1. 选观察者模式当:

  • 需要保证消息必达
  • 系统规模较小
  • 对实时性要求极高

2. 选发布订阅模式当:

  • 需要跨系统通信
  • 订阅方动态变化
  • 需要消息持久化

六、常见误区解析

误区1:观察者模式是发布订阅的简化版

事实:两者是不同维度的设计,发布订阅可以基于观察者实现,但更强调解耦和扩展性。

误区2:RabbitMQ是观察者模式的实现

事实:消息队列是典型的发布订阅实现,通过Exchange路由消息,与观察者模式有本质区别。

误区3:Vue的响应式系统是发布订阅

事实:Vue采用观察者模式实现数据监听,依赖收集器(Dep)相当于被观察者,Watcher就是观察者。

理解这两种模式的差异,关键在于抓住通信机制耦合程度这两个核心要素。下次当有人再混淆这两个概念时,你可以自信地说:"观察者模式就像直接打电话,发布订阅模式就像用微信群发消息!"