1. 设计模式的前世今生

想象你正在组装乐高玩具。当你遇到重复的拼装步骤时,肯定会想要找到某种"固定套路"来提高效率。在编程世界里,设计模式就是这样的"拼装秘籍"。

在Node.js的异步王国里,有两位重量级选手特别值得关注:观察者模式就像敏锐的新闻记者,时刻捕捉事件动态;中介者模式则像老练的外交官,在复杂对象间斡旋调停。而它们共同的舞台,就是Node.js自带的事件系统。

2. 观察者模式的深度剖析

2.1 基础版观察者实现

// Node.js原生实现(技术栈:Node.js 18+)
class NewsPublisher {
  constructor() {
    this.subscribers = new Map();
  }

  // 订阅特定新闻类型
  subscribe(type, callback) {
    if (!this.subscribers.has(type)) {
      this.subscribers.set(type, new Set());
    }
    this.subscribers.get(type).add(callback);
  }

  // 取消订阅
  unsubscribe(type, callback) {
    if (this.subscribers.has(type)) {
      this.subscribers.get(type).delete(callback);
    }
  }

  // 发布新闻
  publish(type, content) {
    if (this.subscribers.has(type)) {
      this.subscribers.get(type).forEach(callback => {
        callback(content);
      });
    }
  }
}

// 使用示例
const CNN = new NewsPublisher();

// 体育新闻订阅者
const sportsFan = news => console.log(`[体育迷] 收到消息: ${news}`);
CNN.subscribe('sports', sportsFan);

// 突发新闻订阅
CNN.subscribe('breaking', news => 
  console.log(`[紧急通知] ${new Date().toISOString()} ${news}`));

// 发布不同类型消息
CNN.publish('sports', '湖人队夺得总冠军');
CNN.publish('breaking', '国际空间站发现新型微生物');

2.2 模式进阶改造

在实际项目中,我们往往需要更精细的控制:

class EnhancedPublisher extends NewsPublisher {
  // 添加订阅次数统计
  subscribe(type, callback) {
    super.subscribe(type, callback);
    console.log(`订阅成功,当前${type}类型订阅数:${this.subscribers.get(type).size}`);
  }

  // 带优先级的发布控制
  publishWithPriority(type, content, level = 'normal') {
    const callbacks = Array.from(this.subscribers.get(type) || []);
    if (level === 'urgent') {
      callbacks.reverse(); // 紧急消息倒序执行
    }
    callbacks.forEach(cb => cb(content));
  }
}

// 验证优先级功能
const BBC = new EnhancedPublisher();
BBC.subscribe('alert', m => console.log('普通处理:', m));
BBC.subscribe('alert', m => console.log('优先处理:', m));
BBC.publishWithPriority('alert', '服务器过载警告', 'urgent');

2.3 应用场景与注意要点

这个模式在以下场景大显身手:

  • WebSocket消息推送系统
  • 跨组件状态同步(如Redux)
  • 实时数据监控仪表盘

需要特别注意的是:

  1. 避免循环通知(在事件处理中触发新事件)
  2. 异步回调可能导致执行顺序不可控
  3. 内存泄漏风险(长期持有回调引用)

3. 中介者模式的精妙舞步

3.1 聊天室实战案例

// 聊天室实现(技术栈:Node.js 18+)
class ChatMediator {
  constructor() {
    this.users = new Map();
    this.messageHistory = [];
  }

  register(user) {
    this.users.set(user.id, user);
    user.setMediator(this);
  }

  sendMessage(senderId, content) {
    const timestamp = new Date().toISOString();
    const message = { senderId, content, timestamp };
    
    // 历史记录存储
    this.messageHistory.push(message);
    
    // 广播给其他用户
    this.users.forEach(user => {
      if (user.id !== senderId) {
        user.receive(message);
      }
    });
  }
}

class ChatUser {
  constructor(id) {
    this.id = id;
    this.mediator = null;
  }

  setMediator(mediator) {
    this.mediator = mediator;
  }

  send(content) {
    console.log(`${this.id} 发送消息...`);
    this.mediator.sendMessage(this.id, content);
  }

  receive(message) {
    console.log(`${this.id} 收到来自${message.senderId}的消息: ${message.content}`);
  }
}

// 创建聊天室
const chatRoom = new ChatMediator();
const alice = new ChatUser('Alice');
const bob = new ChatUser('Bob');

// 注册用户
chatRoom.register(alice);
chatRoom.register(bob);

// 消息交互
alice.send('今晚团建?');
bob.send('好的,几点集合?');

3.2 模式变体与优化

在复杂系统中可以扩展更多功能:

class AdvancedMediator extends ChatMediator {
  // 添加消息过滤
  sendMessage(senderId, content) {
    if (this.isSensitive(content)) {
      console.log(`检测到敏感词: ${senderId}`);
      return;
    }
    super.sendMessage(senderId, content);
  }

  isSensitive(text) {
    const forbiddenWords = ['赌博', '暴力'];
    return forbiddenWords.some(word => text.includes(word));
  }
}

// 测试敏感词过滤
const safeChat = new AdvancedMediator();
safeChat.register(new ChatUser('Charlie'));
safeChat.sendMessage('Charlie', '这个赌博网站...'); // 触发过滤

3.3 适用场景与优劣分析

该模式特别适合:

  • 多方通信的物联网系统
  • 企业级审批流程系统
  • 分布式服务协调

优点显而易见:

  • 简化对象间网状依赖
  • 集中控制通信逻辑
  • 便于扩展新功能

但也需注意:

  • 中介者可能成为性能瓶颈
  • 过度中心化可能违反SRP原则

4. Node.js事件系统的内核奥秘

4.1 EventEmitter魔法揭秘

// Node.js原生事件模块(技术栈:Node.js 18+)
const EventEmitter = require('events');
const util = require('util');

// 自定义温度传感器
class TemperatureSensor extends EventEmitter {
  constructor() {
    super();
    this.currentTemp = 25;
    setInterval(() => this.checkTemperature(), 1000);
  }

  checkTemperature() {
    const fluctuation = Math.random() * 2 - 1;
    this.currentTemp += fluctuation;
    
    if (this.currentTemp > 28) {
      this.emit('overheat', this.currentTemp);
    } else if (this.currentTemp < 22) {
      this.emit('undercool', this.currentTemp);
    }
  }
}

// 创建监控系统
const sensor = new TemperatureSensor();

sensor.on('overheat', temp => 
  console.error(`温度过高告警:${temp.toFixed(1)}℃`));

sensor.on('undercool', temp => 
  console.warn(`低温警告:${temp.toFixed(1)}℃`));

// 添加一次性监听器
sensor.once('overheat', () => 
  console.log('首次触发高温警报'));

4.2 高级事件控制技巧

// 带流量控制的事件发射器
class ThrottledEmitter extends EventEmitter {
  constructor(interval = 1000) {
    super();
    this.lastEmit = Date.now();
    this.interval = interval;
  }

  emit(event, ...args) {
    const now = Date.now();
    if (now - this.lastEmit >= this.interval) {
      super.emit(event, ...args);
      this.lastEmit = now;
    }
  }
}

// 测试频率控制
const controlledEmitter = new ThrottledEmitter(2000);
controlledEmitter.on('ping', () => console.log('收到ping'));

setInterval(() => {
  controlledEmitter.emit('ping');
}, 500); // 实际每2秒才会触发一次

4.3 系统机制深度解析

Node.js的事件循环就像精密的瑞士钟表:

  • 定时器阶段:处理setTimeout/setInterval
  • I/O回调阶段:处理网络请求等异步操作
  • 闲置/准备阶段:内部使用
  • 轮询阶段:检索新的I/O事件
  • 检查阶段:执行setImmediate回调
  • 关闭事件:处理socket关闭等

在编写事件监听时要注意:

  1. 避免在高峰时产生"雪崩效应"
  2. 及时移除无用监听器(memory leak)
  3. 合理使用同步/异步事件触发

5. 综合对比与选型指南

5.1 模式间的血缘关系

观察者模式与事件系统的关系,就像手写信和电子邮件:

  • EventEmitter是观察者模式的标准化实现
  • 中介者模式常结合观察者模式使用
  • 事件系统具备更完善的管理功能

5.2 选型决策树

当遇到通信需求时,可参考以下流程:

  1. 通信双方是否直接相关?→ 否 → 中介者模式
  2. 需要一对多通知机制?→ 是 → 观察者模式
  3. 需要完善的事件管理?→ 是 → EventEmitter

5.3 最佳实践原则

  • 模块化通信:保持事件类型的命名空间化
  • 防御式编程:总是处理error事件
  • 性能监控:使用emitter.listeners()检查监听器数量
  • 错误隔离:用domain模块或process.on('uncaughtException')

6. 模式启示录:未来与演进

随着TypeScript的普及,类型安全的事件系统开始流行:

// 类型安全的Emitter示例
interface AppEvents {
  login: (userId: string) => void;
  logout: (reason: string) => void;
}

class TypedEmitter extends EventEmitter {
  emit<K extends keyof AppEvents>(
    event: K, 
    ...args: Parameters<AppEvents[K]>
  ): boolean {
    return super.emit(event, ...args);
  }
}

微服务架构下,这些模式正在向分布式形态演进:

  • 观察者模式 → 消息队列(RabbitMQ/Kafka)
  • 中介者模式 → 服务网格(Istio/Linkerd)
  • 事件系统 → 服务端事件(SSE/WebSocket)