中介者模式

通过中介者对象协调多个对象之间的交互

问题

什么是中介者模式?如何在 JavaScript 中实现?

解答

中介者模式用一个中介对象来封装一系列对象之间的交互,使对象之间不直接引用,从而降低耦合度。

基础实现

// 中介者
class Mediator {
  constructor() {
    this.colleagues = {}
  }

  // 注册同事对象
  register(name, colleague) {
    this.colleagues[name] = colleague
    colleague.mediator = this
  }

  // 转发消息
  send(message, from, to) {
    if (to) {
      // 发送给指定对象
      this.colleagues[to]?.receive(message, from)
    } else {
      // 广播给所有人
      Object.keys(this.colleagues).forEach(name => {
        if (name !== from) {
          this.colleagues[name].receive(message, from)
        }
      })
    }
  }
}

// 同事类
class Colleague {
  constructor(name) {
    this.name = name
    this.mediator = null
  }

  send(message, to) {
    console.log(`${this.name} 发送: ${message}`)
    this.mediator.send(message, this.name, to)
  }

  receive(message, from) {
    console.log(`${this.name} 收到来自 ${from} 的消息: ${message}`)
  }
}

// 使用
const mediator = new Mediator()

const alice = new Colleague('Alice')
const bob = new Colleague('Bob')
const charlie = new Colleague('Charlie')

mediator.register('Alice', alice)
mediator.register('Bob', bob)
mediator.register('Charlie', charlie)

alice.send('你好', 'Bob')        // 私聊
bob.send('大家好')               // 广播

聊天室示例

class ChatRoom {
  constructor() {
    this.users = new Map()
  }

  join(user) {
    this.users.set(user.name, user)
    user.chatRoom = this
    this.broadcast(`${user.name} 加入了聊天室`, 'System')
  }

  leave(user) {
    this.users.delete(user.name)
    this.broadcast(`${user.name} 离开了聊天室`, 'System')
  }

  sendMessage(message, from, to) {
    const target = this.users.get(to)
    if (target) {
      target.receive(message, from)
    }
  }

  broadcast(message, from) {
    this.users.forEach((user, name) => {
      if (name !== from) {
        user.receive(message, from)
      }
    })
  }
}

class User {
  constructor(name) {
    this.name = name
    this.chatRoom = null
  }

  send(message, to) {
    if (to) {
      this.chatRoom.sendMessage(message, this.name, to)
    } else {
      this.chatRoom.broadcast(message, this.name)
    }
  }

  receive(message, from) {
    console.log(`[${this.name}] ${from}: ${message}`)
  }
}

// 使用
const room = new ChatRoom()

const user1 = new User('张三')
const user2 = new User('李四')
const user3 = new User('王五')

room.join(user1)
room.join(user2)
room.join(user3)

user1.send('大家好!')           // 广播
user2.send('你好张三', '张三')   // 私聊

事件中介者

class EventMediator {
  constructor() {
    this.events = {}
  }

  // 订阅事件
  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = []
    }
    this.events[event].push(callback)
    
    // 返回取消订阅函数
    return () => this.off(event, callback)
  }

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

  // 发布事件
  emit(event, data) {
    if (!this.events[event]) return
    this.events[event].forEach(callback => callback(data))
  }
}

// 组件通过中介者通信
const mediator = new EventMediator()

// 组件 A
const componentA = {
  init() {
    mediator.on('dataUpdate', data => {
      console.log('A 收到数据更新:', data)
    })
  },
  updateData(data) {
    mediator.emit('dataUpdate', data)
  }
}

// 组件 B
const componentB = {
  init() {
    mediator.on('dataUpdate', data => {
      console.log('B 收到数据更新:', data)
    })
  }
}

componentA.init()
componentB.init()
componentA.updateData({ value: 100 })

关键点

  • 解耦对象:对象之间不直接通信,通过中介者转发
  • 集中控制:交互逻辑集中在中介者中,便于维护
  • 适用场景:聊天室、表单联动、组件通信、状态管理
  • 与观察者区别:观察者是一对多广播,中介者是多对多协调
  • 注意事项:中介者可能变得复杂,需要合理拆分