观察者模式与发布订阅模式

观察者模式是 GoF《设计模式》23 种里的行为型模式。一个目标对象维护一组依赖于它的观察者,目标状态变化时主动发出通知,观察者收到后执行更新逻辑。它适合事件监听、状态通知、响应式更新这类一对多通知。

GoF 没有单独收录发布订阅。工程里常把「中间加一层消息中心」的实现叫做发布订阅:发布者和订阅者不直接持有对方,只和消息中心打交道,解耦比观察者更彻底。

观察者模式

观察者模式中,通常至少存在两种角色:

  • Subject:被观察者、目标对象
  • Observer:观察者

一个 Subject 可以被多个 Observer 观察。当 Subject 的状态发生变化时,它会主动通知所有已注册的 Observer,Observer 接收到通知后执行自己的更新逻辑。

Subject 一般需要提供三类能力:

  • 添加观察者
  • 移除观察者
  • 通知观察者

观察者与被观察者之间是直接建立关联的。观察者订阅目标后,目标就持有观察者的引用;目标状态变化时,直接调用观察者的通知方法。

优点

  • 响应式自然:目标变化后自动通知观察者,适合状态驱动 UI、事件监听等场景。
  • 实现简单:核心逻辑围绕 Subject 和 Observer 展开,结构清晰。

缺点

  • 耦合较强:观察者和被观察者之间存在直接依赖。
  • 灵活性有限:如果发布方和订阅方需要跨模块、跨系统通信,直接观察者模式会变得不够灵活。
// 被观察者
class Subject {
  constructor() {
    this.obs = [];
  }

  add(ob) {
    this.obs.push(ob);
  }

  remove(ob) {
    this.obs = this.obs.filter(o => o !== ob);
  }

  notify(message) {
    this.obs.forEach(ob => ob.notified(message));
  }
}

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

  notified(message) {
    console.log(`Hello, ${this.name}. This is ${message}!`);
  }
}

const subject = new Subject();
const observerLi = new Observer('Li');
const observerZhao = new Observer('Zhao');

subject.add(observerLi);
subject.add(observerZhao);

subject.notify('Subject message 01');

subject.remove(observerLi);

subject.notify('Subject message 02');

发布订阅模式

发布订阅模式的核心是引入一个消息中心(事件中心、EventBus)。

发布者和订阅者不直接通信。发布者把消息交给消息中心,订阅者从消息中心订阅自己关心的消息类型。发布者不需要知道谁订阅了消息,订阅者也不需要知道谁发布了消息。

这种模式更关注「事件本身」,而不是发布者和订阅者之间的直接关系。

优点

  • 解耦明显:发布者和订阅者不直接依赖。
  • 灵活性强:只要引入消息中心,发布和订阅可以分散在不同模块、不同页面,甚至不同系统中。
  • 适合跨模块通信:比如全局事件、消息推送、组件间通信、跨页面通信。

缺点

  • 维护成本上升:事件类型如果没有良好管理,容易造成数据流混乱。
  • 调试难度增加:消息被谁订阅、何时发布,不如直接函数调用直观。
  • 性能与内存消耗:事件中心需要维护订阅队列,订阅者越多,消息分发成本越高。
// 消息中心
class PubSub {
  constructor() {
    this.pubs = {};
    this.subs = {};
  }

  publish(type, content) {
    this.pubs[type] = this.pubs[type] || [];
    this.pubs[type].push(content);
  }

  subscribe(type, cb) {
    this.subs[type] = this.subs[type] || [];
    this.subs[type].push(cb);
  }

  notify(type) {
    const pubs = this.pubs[type] || [];
    const subs = this.subs[type] || [];

    subs.forEach(cb => {
      pubs.forEach(msg => cb(msg));
    });
  }
}

// 发布者
class Publisher {
  constructor(name, context) {
    this.name = name;
    this.context = context;
  }

  publish(content) {
    this.context.publish(this.name, content);
  }
}

// 订阅者
class Subscriber {
  constructor(context) {
    this.context = context;
  }

  subscribe(type, cb) {
    this.context.subscribe(type, cb);
  }
}

const Type1 = 'music';
const Type2 = 'movie';

const pubsub = new PubSub();

const pubA = new Publisher(Type1, pubsub);
const pubB = new Publisher(Type2, pubsub);

const subA = new Subscriber(pubsub);
const subB = new Subscriber(pubsub);

subA.subscribe(Type1, msg => console.log(`I am A. ${msg} is published!`));
subB.subscribe(Type2, msg => console.log(`I am B. ${msg} is published!`));

pubA.publish('music01!');
pubA.publish('music02!');
pubsub.notify(Type1);

pubB.publish('movie01!');
pubsub.notify(Type2);

注意:上面的示例先把消息存进中心,再由 notify 发给当前订阅者;再调一次 notify,历史消息会再发一遍。常见的 EventBus(如 Node.js 的 EventEmitter)是 publishemit 时立刻通知当前订阅者,默认不重放旧消息。

观察者模式 vs 发布订阅模式

两者在概念上很相似,都是「某个事件发生后,通知相关方执行更新」。但它们在实现思想和适用场景上有明显区别。

调度方式不同:

  • 观察者模式:由被观察者直接调度观察者。
  • 发布订阅模式:由消息中心统一调度发布和订阅。

角色数量不同:

  • 观察者模式:主要是两个角色,SubjectObserver
  • 发布订阅模式:至少需要三个角色,PublisherSubscriber 和消息中心。

耦合度不同:

  • 观察者模式:目标和观察者直接关联,耦合更强。常说的「解耦」解的是业务代码(目标不必写死每个观察者的具体逻辑),不是目标和观察者本身。
  • 发布订阅模式:发布者和订阅者没有直接关联,全部通过消息中心间接通信,解耦更彻底。

适用场景不同:

  • 观察者模式:更适合单个目标对象内部的状态通知,比如一个数据对象变化后通知多个视图更新。
  • 发布订阅模式:更适合跨模块、跨组件、跨系统的消息通信,比如全局事件总线、消息队列、组件间通信。

总结

观察者模式强调「目标变化,观察者被通知」,适合简单直接的响应式场景。

发布订阅模式强调「发布者发布事件,订阅者订阅事件」,通过消息中心实现更彻底的解耦,适合复杂系统中的事件通信。

参考资料

Design Patterns: Elements of Reusable Object-Oriented Software — Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides(Gang of Four),Addison-Wesley,1994。观察者模式见行为型模式。

Observer pattern(Wikipedia)

Publish–subscribe pattern(Wikipedia)

© lizhao all right reserved,powered by Gitbook文件修订时间: 2026-09-02 23:27:31

results matching ""

    No results matching ""