中介者模式

通过中间人协调多个对象之间的通信

问题

中介者模式是什么?如何实现?用”找媒人介绍对象”来理解。

解答

中介者模式让多个对象不直接相互引用,而是通过一个中介者来协调通信。就像相亲时,男女双方不直接联系,而是通过媒人传话。

基础实现

// 中介者(媒人)
class Matchmaker {
  constructor() {
    this.participants = {}
  }

  // 注册参与者
  register(person) {
    this.participants[person.name] = person
    person.matchmaker = this
  }

  // 转发消息
  send(message, from, to) {
    const target = this.participants[to]
    if (target) {
      target.receive(message, from)
    }
  }

  // 广播消息给所有人
  broadcast(message, from) {
    Object.values(this.participants).forEach(person => {
      if (person.name !== from) {
        person.receive(message, from)
      }
    })
  }
}

// 参与者
class Person {
  constructor(name) {
    this.name = name
    this.matchmaker = null
  }

  // 发送消息(通过媒人)
  send(message, to) {
    console.log(`${this.name} 说: ${message}`)
    this.matchmaker.send(message, this.name, to)
  }

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

// 使用
const matchmaker = new Matchmaker()

const zhangSan = new Person('张三')
const liSi = new Person('李四')
const wangWu = new Person('王五')

matchmaker.register(zhangSan)
matchmaker.register(liSi)
matchmaker.register(wangWu)

zhangSan.send('你好,想认识一下', '李四')
liSi.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')
  }

  sendTo(message, from, to) {
    const user = this.users.get(to)
    if (user) {
      user.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
  }

  say(message) {
    this.chatRoom.broadcast(message, this.name)
  }

  sayTo(message, to) {
    this.chatRoom.sendTo(message, this.name, to)
  }

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

// 使用
const room = new ChatRoom()

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

room.join(alice)
room.join(bob)
room.join(charlie)

alice.say('大家好!')
bob.sayTo('Alice 你好', 'Alice')

表单验证中介者

// 表单中介者:协调多个输入框的验证
class FormMediator {
  constructor() {
    this.fields = {}
    this.submitBtn = null
  }

  registerField(field) {
    this.fields[field.name] = field
    field.mediator = this
  }

  registerSubmitBtn(btn) {
    this.submitBtn = btn
  }

  // 某个字段变化时,检查整体表单状态
  fieldChanged(fieldName) {
    const allValid = Object.values(this.fields).every(f => f.isValid)
    
    if (this.submitBtn) {
      this.submitBtn.disabled = !allValid
    }

    // 可以添加字段间的联动逻辑
    // 比如:密码确认框依赖密码框
    if (fieldName === 'password' && this.fields['confirmPassword']) {
      this.fields['confirmPassword'].validate()
    }
  }
}

class FormField {
  constructor(name, validator) {
    this.name = name
    this.validator = validator
    this.value = ''
    this.isValid = false
    this.mediator = null
  }

  setValue(value) {
    this.value = value
    this.validate()
  }

  validate() {
    this.isValid = this.validator(this.value, this.mediator?.fields)
    this.mediator?.fieldChanged(this.name)
    return this.isValid
  }
}

// 使用
const form = new FormMediator()

const username = new FormField('username', v => v.length >= 3)
const password = new FormField('password', v => v.length >= 6)
const confirmPassword = new FormField('confirmPassword', (v, fields) => {
  return v === fields?.password?.value
})

form.registerField(username)
form.registerField(password)
form.registerField(confirmPassword)

username.setValue('tom')
password.setValue('123456')
confirmPassword.setValue('123456')

关键点

  • 解耦对象:对象之间不直接通信,通过中介者转发,降低耦合度
  • 集中控制:交互逻辑集中在中介者中,便于维护和修改
  • 适用场景:聊天室、表单联动、GUI 组件交互、飞机塔台调度
  • 缺点:中介者可能变得复杂,成为”上帝对象”
  • 与观察者模式区别:观察者是一对多广播,中介者是多对多协调