实现观察者模式

手写观察者模式并理解其应用场景

问题

什么是观察者模式?如何实现?它和发布订阅模式有什么区别?

解答

观察者模式定义

观察者模式定义了对象间一对多的依赖关系。当一个对象(Subject)状态改变时,所有依赖它的对象(Observer)都会收到通知并自动更新。

基础实现

// 被观察者(Subject)
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));
  }
}

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

  // 收到通知时的处理方法
  update(data) {
    console.log(`${this.name} 收到通知:`, data);
  }
}

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

const observer1 = new Observer('观察者1');
const observer2 = new Observer('观察者2');

subject.addObserver(observer1);
subject.addObserver(observer2);

subject.notify('状态已更新'); 
// 观察者1 收到通知: 状态已更新
// 观察者2 收到通知: 状态已更新

subject.removeObserver(observer1);
subject.notify('再次更新');
// 观察者2 收到通知: 再次更新

实际应用:数据绑定

// 模拟 Vue 响应式原理
class ReactiveData {
  constructor(initialValue) {
    this._value = initialValue;
    this.observers = [];
  }

  get value() {
    return this._value;
  }

  set value(newValue) {
    if (this._value !== newValue) {
      this._value = newValue;
      // 值改变时通知所有观察者
      this.observers.forEach(fn => fn(newValue));
    }
  }

  // 订阅变化
  watch(callback) {
    this.observers.push(callback);
    // 返回取消订阅的函数
    return () => {
      this.observers = this.observers.filter(fn => fn !== callback);
    };
  }
}

// 使用示例
const count = new ReactiveData(0);

// 模拟视图更新
const unwatch = count.watch(value => {
  console.log('视图更新,当前值:', value);
});

count.value = 1; // 视图更新,当前值: 1
count.value = 2; // 视图更新,当前值: 2

unwatch(); // 取消订阅
count.value = 3; // 无输出

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

// 发布订阅模式 - 有中间调度中心
class EventEmitter {
  constructor() {
    this.events = {}; // 事件中心
  }

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

  // 发布事件
  emit(event, ...args) {
    const callbacks = this.events[event];
    if (callbacks) {
      callbacks.forEach(cb => cb(...args));
    }
  }

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

// 使用示例
const emitter = new EventEmitter();

// 订阅者不需要知道发布者是谁
emitter.on('userLogin', user => {
  console.log('记录日志:', user.name);
});

emitter.on('userLogin', user => {
  console.log('发送欢迎邮件:', user.email);
});

// 发布者不需要知道订阅者是谁
emitter.emit('userLogin', { name: '张三', email: 'zhang@example.com' });
// 记录日志: 张三
// 发送欢迎邮件: zhang@example.com

两者区别图示

观察者模式:
Subject ----直接通知----> Observer1
       ----直接通知----> Observer2
(Subject 和 Observer 相互知道对方)

发布订阅模式:
Publisher --> EventCenter --> Subscriber1
                          --> Subscriber2
(Publisher 和 Subscriber 完全解耦)

关键点

  • 观察者模式:Subject 直接持有 Observer 引用,两者存在依赖关系
  • 发布订阅模式:通过事件中心解耦,发布者和订阅者互不知道对方
  • 应用场景:DOM 事件、Vue 响应式、Redux 状态管理、Node EventEmitter
  • 优点:解耦对象间的依赖,支持广播通信
  • 缺点:观察者过多时可能影响性能,调试时难以追踪通知链路