阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 观察者模式(Observer)与发布/订阅的区别

观察者模式(Observer)与发布/订阅的区别

作者:陈川 阅读数:4409人阅读 分类: JavaScript

观察者模式(Observer)和发布/订阅模式(Pub/Sub)都是用于处理对象间一对多依赖关系的设计模式,但它们在实现方式和耦合度上有显著差异。理解这两种模式的区别对于选择合适的事件处理机制至关重要。

观察者模式的核心机制

观察者模式定义了一种一对多的依赖关系,当一个对象(称为Subject)的状态发生改变时,所有依赖于它的对象(称为Observers)都会自动收到通知并更新。这种模式中,Subject和Observer之间存在直接关系。

// 观察者模式实现
class Subject {
  constructor() {
    this.observers = [];
  }

  addObserver(observer) {
    this.observers.push(observer);
  }

  removeObserver(observer) {
    this.observers = this.observers.filter(obs => obs !== observer);
  }

  notify(data) {
    this.observers.forEach(observer => observer.update(data));
  }
}

class Observer {
  update(data) {
    console.log(`Observer received: ${data}`);
  }
}

// 使用示例
const subject = new Subject();
const observer1 = new Observer();
const observer2 = new Observer();

subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify('State changed!');

发布/订阅模式的核心机制

发布/订阅模式通过引入消息代理(通常是事件通道)来解耦发布者和订阅者。发布者不直接通知订阅者,而是将消息发布到通道,由通道负责将消息传递给订阅者。

// 发布/订阅模式实现
class EventBus {
  constructor() {
    this.events = {};
  }

  subscribe(eventName, callback) {
    if (!this.events[eventName]) {
      this.events[eventName] = [];
    }
    this.events[eventName].push(callback);
  }

  unsubscribe(eventName, callback) {
    if (this.events[eventName]) {
      this.events[eventName] = this.events[eventName].filter(
        cb => cb !== callback
      );
    }
  }

  publish(eventName, data) {
    if (this.events[eventName]) {
      this.events[eventName].forEach(callback => {
        callback(data);
      });
    }
  }
}

// 使用示例
const eventBus = new EventBus();

const subscription1 = data => console.log(`Subscriber 1: ${data}`);
const subscription2 = data => console.log(`Subscriber 2: ${data}`);

eventBus.subscribe('news', subscription1);
eventBus.subscribe('news', subscription2);
eventBus.publish('news', 'Latest update!');

两种模式的关键区别

  1. 耦合程度

    • 观察者模式中,Subject和Observer彼此知道对方的存在
    • 发布/订阅模式中,发布者和订阅者完全不知道对方的存在,通过事件通道通信
  2. 同步性

    • 观察者模式通常是同步的,当Subject调用notify()时,所有Observer会立即更新
    • 发布/订阅模式可以是异步的,消息可以存储在队列中稍后处理
  3. 实现复杂度

    • 观察者模式相对简单,适合小型应用或组件内部通信
    • 发布/订阅模式更复杂但更灵活,适合大型应用或跨组件通信
  4. 错误处理

    • 观察者模式中,一个Observer出错可能影响整个通知链
    • 发布/订阅模式中,错误通常被隔离在单个订阅者中

实际应用场景对比

观察者模式适用场景

  • Vue.js的响应式系统
  • 小型UI组件间的状态同步
  • 需要立即响应的场景
// Vue响应式原理简化版
class Dep {
  constructor() {
    this.subscribers = [];
  }
  depend() {
    if (target && !this.subscribers.includes(target)) {
      this.subscribers.push(target);
    }
  }
  notify() {
    this.subscribers.forEach(sub => sub());
  }
}

let target = null;

function watcher(myFunc) {
  target = myFunc;
  target();
  target = null;
}

发布/订阅模式适用场景

  • 跨组件或跨模块通信
  • 需要中间件处理消息的场景(如日志、验证)
  • 需要支持多种消息类型的系统
// Redux的store实现原理简化版
function createStore(reducer) {
  let state;
  const listeners = [];
  
  const getState = () => state;
  
  const dispatch = (action) => {
    state = reducer(state, action);
    listeners.forEach(listener => listener());
  };
  
  const subscribe = (listener) => {
    listeners.push(listener);
    return () => {
      listeners = listeners.filter(l => l !== listener);
    };
  };
  
  dispatch({});
  
  return { getState, dispatch, subscribe };
}

性能考量

  1. 内存使用

    • 观察者模式中,Subject维护着Observer的引用列表
    • 发布/订阅模式中,事件通道可能存储大量订阅关系
  2. 执行效率

    • 观察者模式直接调用方法,效率更高
    • 发布/订阅模式需要查找事件类型对应的回调,稍慢
  3. 扩展性

    • 观察者模式在Observer数量增长时可能遇到性能问题
    • 发布/订阅模式更容易扩展,可以引入多个事件通道

模式变体与混合使用

实践中经常看到两种模式的混合实现。例如,RxJS的Observable结合了两种模式的特点:

// RxJS示例
import { Subject } from 'rxjs';

const subject = new Subject();

const subscription1 = subject.subscribe({
  next: (v) => console.log(`observerA: ${v}`)
});

const subscription2 = subject.subscribe({
  next: (v) => console.log(`observerB: ${v}`)
});

subject.next(1);
subject.next(2);

subscription1.unsubscribe();

选择模式的决策因素

  1. 系统规模

    • 小型系统可能更适合简单的观察者模式
    • 大型分布式系统通常需要发布/订阅模式
  2. 组件关系

    • 紧密耦合的组件间通信可用观察者模式
    • 松散耦合的模块间通信适合发布/订阅
  3. 消息处理需求

    • 需要消息过滤、转换或持久化时选择发布/订阅
    • 简单通知机制观察者模式更直接
  4. 调试需求

    • 观察者模式调用链路更清晰易调试
    • 发布/订阅模式由于间接性可能更难追踪问题

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌