解释观察者模式,如何实现观察者模式?

发布于:2025-04-06 ⋅ 阅读:(18) ⋅ 点赞:(0)

一、模式本质

观察者模式(Observer Pattern)建立​​对象间的一对多依赖关系​​,当核心对象(Subject)状态变化时,自动通知所有订阅者(Observers)。

这是一种​​推模型​​的典型实现,常用于解耦生产者和消费者。

二、核心实现(TypeScript)

// 抽象主题接口
interface Subject {
  addObserver(obs: Observer): void;
  removeObserver(obs: Observer): void;
  notifyObservers(): void;
}

// 具体主题实现
class ConcreteSubject implements Subject {
  private observers: Observer[] = [];
  private state: number = 0;

  // 添加观察者时进行引用校验
  addObserver(obs: Observer): void {
    if (!this.observers.includes(obs)) {
      this.observers.push(obs);
    } else {
      console.warn('Observer already exists');
    }
  }

  // 使用过滤器避免splice索引问题
  removeObserver(obs: Observer): void {
    const initialLength = this.observers.length;
    this.observers = this.observers.filter(o => o !== obs);
    if (initialLength === this.observers.length) {
      console.warn('Observer not found');
    }
  }

  notifyObservers(): void {
    // 克隆数组防止迭代过程中被修改
    const observersCopy = [...this.observers];
    observersCopy.forEach(obs => obs.update(this.state));
  }

  // 业务方法触发状态变化
  setState(newState: number): void {
    this.state = newState;
    this.notifyObservers();
  }
}

// 观察者接口
interface Observer {
  update(state: number): void;
}

// 具体观察者
class ConcreteObserver implements Observer {
  constructor(private name: string) {}

  update(state: number): void {
    console.log(`[${this.name}] Received state update:`, state);
    // 这里可以触发视图更新等操作
  }
}

三、应用场景建议

1. 复杂表单联动验证

// 表单字段基类
abstract class FormField {
  private validators: Validator[] = [];
  
  addValidator(v: Validator) {
    this.validators.push(v);
  }

  validate() {
    const errors = this.validators.map(v => v.validate(this.value));
    return errors.filter(e => e !== null);
  }
}

// 实际字段实现
class EmailField extends FormField {
  value: string = '';
}

// 验证器接口
interface Validator {
  validate(value: any): string | null;
}

// 使用示例
const emailField = new EmailField();
emailField.addValidator({
  validate: (value) => !/.+@.+\..+/.test(value) ? 'Invalid email' : null
});

2. WebSocket消息广播

class WebSocketManager implements Subject {
  private static instance: WebSocketManager;
  private ws: WebSocket;
  private observers: Observer[] = [];

  private constructor() {
    this.ws = new WebSocket('wss://api.example.com');
    this.ws.onmessage = (event) => {
      this.notifyObservers(JSON.parse(event.data));
    };
  }

  static getInstance(): WebSocketManager {
    if (!this.instance) {
      this.instance = new WebSocketManager();
    }
    return this.instance;
  }

  // 实现Subject接口方法...
}

3. 复杂状态管理

// 增强型状态管理
class Store<T> implements Subject {
  private state: T;
  private observers: Observer[] = [];

  constructor(initialState: T) {
    this.state = initialState;
  }

  setState(newState: Partial<T>) {
    this.state = { ...this.state, ...newState };
    this.notifyObservers();
  }

  // 支持选择器订阅
  subscribe(selector: (state: T) => any, callback: (value: any) => void) {
    const observer = {
      update: () => {
        const selected = selector(this.state);
        callback(selected);
      }
    };
    this.addObserver(observer);
    return () => this.removeObserver(observer);
  }
}

四、关键注意事项

1. 内存泄漏防护

// 使用WeakMap避免强引用
const observerMap = new WeakMap<Subject, Set<Observer>>();

class SafeSubject implements Subject {
  constructor() {
    observerMap.set(this, new Set());
  }

  addObserver(obs: Observer) {
    observerMap.get(this)?.add(obs);
  }

  // 自动清理无效引用
  notifyObservers() {
    const observers = observerMap.get(this);
    if (!observers) return;
    
    for (const obs of observers) {
      if (typeof obs.update !== 'function') {
        observers.delete(obs);
      } else {
        obs.update(this.state);
      }
    }
  }
}

2. 批量更新优化

class BatchedSubject extends ConcreteSubject {
  private updateQueue = new Set<Observer>();
  private isBatching = false;

  notifyObservers() {
    if (this.isBatching) return;
    
    this.isBatching = true;
    requestAnimationFrame(() => {
      super.notifyObservers();
      this.isBatching = false;
      this.updateQueue.clear();
    });
  }

  // 重写状态更新方法
  setState(newState: number) {
    super.setState(newState);
    if (this.isBatching) {
      this.updateQueue.add(...this.observers);
    }
  }
}

3. 异步通知处理

class AsyncSubject extends ConcreteSubject {
  async notifyObservers() {
    const promises = this.observers.map(async obs => {
      try {
        await obs.update(this.state);
      } catch (error) {
        console.error('Observer error:', error);
      }
    });
    await Promise.allSettled(promises);
  }
}

五、模式对比

特性 观察者模式 发布-订阅模式
耦合程度 直接引用 通过中间层
通信方式 同步/异步 通常异步
关系复杂度 1:N M:N
典型应用 对象状态通知 系统级别事件
内存管理难度 较高 较低

六、最佳实践建议

  1. ​优先使用组合​​:通过构造函数注入观察者

    class DataLoader {
      constructor(private notifier: Subject) {}
      
      async load() {
        try {
          const data = await fetchData();
          this.notifier.setState({ data });
        } catch (error) {
          this.notifier.setState({ error });
        }
      }
    }
  2. ​防御性编程​​:添加观察者生命周期管理

    interface Observer {
      update(state: any): void;
      destroy?(): void;
    }
    
    class SafeSubject {
      private observers = new Set<Observer>();
    
      notifyObservers() {
        this.observers.forEach(obs => {
          if (typeof obs.update === 'function') {
            try {
              obs.update(this.state);
            } catch (error) {
              console.error('Observer error:', error);
              if (typeof obs.destroy === 'function') {
                obs.destroy();
                this.observers.delete(obs);
              }
            }
          }
        });
      }
    }
  3. ​性能监控​​:添加观察者执行耗时统计

    class InstrumentedSubject extends ConcreteSubject {
      notifyObservers() {
        this.observers.forEach(obs => {
          const start = performance.now();
          obs.update(this.state);
          const duration = performance.now() - start;
          if (duration > 100) {
            console.warn(`Slow observer: ${obs.constructor.name} took ${duration}ms`);
          }
        });
      }
    }

七、常见面试问题

  1. ​如何防止观察者执行阻塞主线程?​

    • 答:采用异步通知机制,使用微任务队列或Web Worker
  2. ​观察者模式与响应式编程的关系?​

    • 答:RxJS等库的Observable是观察者模式的演进,增加了流处理能力
  3. ​如何处理观察者之间的依赖关系?​

    • 答:引入优先级机制或拓扑排序,但需谨慎处理避免循环依赖
  4. ​在Vue/React中的具体应用?​

    • 答:Vue的响应式系统基于观察者模式,React的Context API可视为变体实现。