13. 观察者模式(Observer)

问题:一个对象状态变化时,需要自动通知多个依赖对象。

核心:目标维护观察者列表,状态变化时遍历通知。

type Listener<T> = (data: T) => void;

class EventEmitter<Events extends Record<string, unknown>> {
  #listeners: { [K in keyof Events]?: Listener<Events[K]>[] } = {};

  on<K extends keyof Events>(event: K, fn: Listener<Events[K]>): this {
    (this.#listeners[event] ??= []).push(fn);
    return this;
  }

  emit<K extends keyof Events>(event: K, data: Events[K]): void {
    (this.#listeners[event] ?? []).forEach(fn => fn(data));
  }
}

const bus = new EventEmitter<{ login: string }>();
bus.on('login', user => console.log(`${user} 已登录`));
bus.on('login', user => console.log(`记录日志: ${user} 登录`));

bus.emit('login', '小明');
// 小明 已登录
// 记录日志: 小明 登录
package observer

import "fmt"

type Observer interface {
  OnNotify(event string, data any)
}

type EventEmitter struct {
  listeners map[string][]Observer
}

func NewEventEmitter() *EventEmitter {
  return &EventEmitter{listeners: make(map[string][]Observer)}
}

func (e *EventEmitter) On(event string, obs Observer) {
  e.listeners[event] = append(e.listeners[event], obs)
}

func (e *EventEmitter) Emit(event string, data any) {
  for _, obs := range e.listeners[event] {
    obs.OnNotify(event, data)
  }
}

// 示例观察者
type Logger struct{}

func (l Logger) OnNotify(event string, data any) {
  fmt.Printf("记录日志: %v %v\n", event, data)
}

实战场景

  • DOM 事件监听:按钮点击、输入变化、滚动、键盘事件都可以被多个监听器订阅。
  • 前端响应式系统:状态变化后通知依赖它的渲染函数、计算属性或 watcher 重新执行。
  • 事件总线:跨组件、跨模块发送业务事件,例如登录成功后刷新用户信息、关闭弹窗、记录日志。
  • 消息通知:订单状态变化后,同时通知库存、物流、积分、邮件和风控模块。
  • 数据订阅:实时行情、协作文档、在线状态变化推送给多个 UI 面板。
  • 后端领域事件:用户注册、支付成功、发票开具等事件发布后,由多个 handler 各自处理副作用。
  • 文件监听:开发服务器监听文件变化,通知编译、刷新浏览器、重新生成路由。
  • 表单联动:一个字段变化后,通知依赖字段重新校验、显隐或更新可选项。
  • 监控告警:指标超过阈值时,同时触发告警、记录事件、创建工单或执行自愈脚本。
  • 插件系统:核心应用发布生命周期事件,插件按需订阅启动、保存、发布、销毁等节点。

DOM 的 addEventListener、Node.js 的 EventEmitter、Vue 的响应式系统都是观察者模式。