Skip to content

中介者模式(Mediator Pattern)

概念

中介者模式是一种行为设计模式,它定义一个中介对象来封装一系列对象之间的交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

中介者模式的核心思想是将多对多的对象交互关系转换为一对多的关系,通过引入一个中介者对象来集中处理对象间的复杂交互,从而降低系统的耦合度,提高系统的可维护性和可扩展性。

基本实现

中介者模式包含以下主要角色:

  1. Mediator(抽象中介者):定义一个接口用于与各同事对象通信。
  2. ConcreteMediator(具体中介者):实现抽象中介者接口,了解并维护各个同事对象,协调它们之间的交互。
  3. Colleague(抽象同事类):定义各同事类的公有方法,并声明中介者对象。
  4. ConcreteColleague(具体同事类):实现抽象同事类,当需要与其他同事通信时,通过中介者进行。

下面是中介者模式的基本实现:

javascript
// 抽象中介者
class Mediator {
  notify(sender, event) {
    throw new Error('notify method must be implemented');
  }
}

// 抽象同事类
class Colleague {
  constructor(mediator) {
    this.mediator = mediator;
  }
}

// 具体同事类A
class ConcreteColleagueA extends Colleague {
  doAction() {
    console.log('同事A执行操作');
    this.mediator.notify(this, 'A');
  }

  doActionA() {
    console.log('同事A执行特定操作A');
  }
}

// 具体同事类B
class ConcreteColleagueB extends Colleague {
  doAction() {
    console.log('同事B执行操作');
    this.mediator.notify(this, 'B');
  }

  doActionB() {
    console.log('同事B执行特定操作B');
  }
}

// 具体中介者
class ConcreteMediator extends Mediator {
  constructor(colleagueA, colleagueB) {
    super();
    this.colleagueA = colleagueA;
    this.colleagueB = colleagueB;
  }

  notify(sender, event) {
    if (sender === this.colleagueA && event === 'A') {
      console.log('中介者协调:A操作触发B操作');
      this.colleagueB.doActionB();
    }

    if (sender === this.colleagueB && event === 'B') {
      console.log('中介者协调:B操作触发A操作');
      this.colleagueA.doActionA();
    }
  }
}

// 使用示例
const colleagueA = new ConcreteColleagueA();
const colleagueB = new ConcreteColleagueB();
const mediator = new ConcreteMediator(colleagueA, colleagueB);

// 设置中介者引用
colleagueA.mediator = mediator;
colleagueB.mediator = mediator;

colleagueA.doAction();
colleagueB.doAction();

实际应用场景

场景1:聊天室系统

聊天室是中介者模式的经典应用场景,用户通过聊天室(中介者)进行消息传递,而不需要直接相互引用。

javascript
// 抽象聊天室中介者
class ChatRoomMediator {
  addUser(user) {
    throw new Error('addUser method must be implemented');
  }

  sendMessage(message, sender) {
    throw new Error('sendMessage method must be implemented');
  }
}

// 具体聊天室中介者
class ChatRoom extends ChatRoomMediator {
  constructor() {
    super();
    this.users = new Map(); // 存储用户
  }

  addUser(user) {
    this.users.set(user.getId(), user);
    user.setChatRoom(this);
    console.log(`用户 ${user.getName()} 加入聊天室`);
  }

  removeUser(user) {
    this.users.delete(user.getId());
    console.log(`用户 ${user.getName()} 离开聊天室`);
  }

  sendMessage(message, sender) {
    console.log(`[${new Date().toLocaleTimeString()}] ${sender.getName()}: ${message}`);
    
    // 将消息发送给所有其他用户
    for (const [userId, user] of this.users) {
      if (userId !== sender.getId()) {
        user.receiveMessage(message, sender);
      }
    }
  }

  sendPrivateMessage(message, sender, receiverId) {
    const receiver = this.users.get(receiverId);
    if (receiver) {
      console.log(`[${new Date().toLocaleTimeString()}] ${sender.getName()} -> ${receiver.getName()}: ${message}`);
      receiver.receivePrivateMessage(message, sender);
    } else {
      console.log(`用户 ${receiverId} 不在线`);
    }
  }
}

// 抽象用户类
class User {
  constructor(id, name) {
    this.id = id;
    this.name = name;
    this.chatRoom = null;
  }

  getId() {
    return this.id;
  }

  getName() {
    return this.name;
  }

  setChatRoom(chatRoom) {
    this.chatRoom = chatRoom;
  }

  sendMessage(message) {
    if (this.chatRoom) {
      this.chatRoom.sendMessage(message, this);
    }
  }

  sendPrivateMessage(message, receiverId) {
    if (this.chatRoom) {
      this.chatRoom.sendPrivateMessage(message, this, receiverId);
    }
  }

  receiveMessage(message, sender) {
    console.log(`[${new Date().toLocaleTimeString()}] 收到来自 ${sender.getName()} 的消息: ${message}`);
  }

  receivePrivateMessage(message, sender) {
    console.log(`[${new Date().toLocaleTimeString()}] 收到来自 ${sender.getName()} 的私信: ${message}`);
  }
}

// 具体用户类
class ChatUser extends User {
  constructor(id, name) {
    super(id, name);
  }

  joinChatRoom(chatRoom) {
    chatRoom.addUser(this);
    this.setChatRoom(chatRoom);
  }

  leaveChatRoom() {
    if (this.chatRoom) {
      this.chatRoom.removeUser(this);
      this.chatRoom = null;
    }
  }
}

// 使用示例
const chatRoom = new ChatRoom();

const user1 = new ChatUser(1, 'Alice');
const user2 = new ChatUser(2, 'Bob');
const user3 = new ChatUser(3, 'Charlie');

// 用户加入聊天室
user1.joinChatRoom(chatRoom);
user2.joinChatRoom(chatRoom);
user3.joinChatRoom(chatRoom);

console.log('\n=== 群聊消息 ===');
user1.sendMessage('大家好!');
user2.sendMessage('你好,Alice!');
user3.sendMessage('欢迎来到聊天室!');

console.log('\n=== 私信 ===');
user1.sendPrivateMessage('Bob,你好吗?', 2);
user2.sendPrivateMessage('我很好,谢谢!', 1);

console.log('\n=== 用户离开 ===');
user3.leaveChatRoom();
user1.sendMessage('Charlie去哪里了?');

场景2:飞机塔台调度系统

在机场塔台调度系统中,塔台作为中介者协调所有飞机的起降,飞机之间不需要直接通信。

javascript
// 抽象塔台中介者
class AirTrafficControl {
  registerAircraft(aircraft) {
    throw new Error('registerAircraft method must be implemented');
  }

  requestTakeoff(aircraft) {
    throw new Error('requestTakeoff method must be implemented');
  }

  requestLanding(aircraft) {
    throw new Error('requestLanding method must be implemented');
  }

  notifyWeatherChange(weather) {
    throw new Error('notifyWeatherChange method must be implemented');
  }
}

// 具体塔台中介者
class AirportControlTower extends AirTrafficControl {
  constructor() {
    super();
    this.aircrafts = new Map(); // 注册的飞机
    this.runways = new Set();   // 可用跑道
    this.weather = '晴朗';       // 天气状况
  }

  // 添加可用跑道
  addRunway(runway) {
    this.runways.add(runway);
    console.log(`跑道 ${runway} 已添加到可用跑道列表`);
  }

  // 移除跑道
  removeRunway(runway) {
    this.runways.delete(runway);
    console.log(`跑道 ${runway} 已从可用跑道列表移除`);
  }

  registerAircraft(aircraft) {
    this.aircrafts.set(aircraft.getId(), aircraft);
    aircraft.setControlTower(this);
    console.log(`飞机 ${aircraft.getFlightNumber()} 已注册到塔台`);
  }

  unregisterAircraft(aircraft) {
    this.aircrafts.delete(aircraft.getId());
    console.log(`飞机 ${aircraft.getFlightNumber()} 已从塔台注销`);
  }

  requestTakeoff(aircraft) {
    if (this.runways.size === 0) {
      console.log(`塔台通知 ${aircraft.getFlightNumber()}: 暂无可用跑道,无法起飞`);
      return false;
    }

    if (this.weather === '暴风雨') {
      console.log(`塔台通知 ${aircraft.getFlightNumber()}: 恶劣天气,禁止起飞`);
      return false;
    }

    const runway = this.runways.values().next().value;
    this.runways.delete(runway);
    
    console.log(`塔台批准 ${aircraft.getFlightNumber()} 在跑道 ${runway} 起飞`);
    aircraft.approveTakeoff(runway);
    return true;
  }

  requestLanding(aircraft) {
    if (this.runways.size === 0) {
      console.log(`塔台通知 ${aircraft.getFlightNumber()}: 暂无可用跑道,等待降落`);
      return false;
    }

    if (this.weather === '暴风雨') {
      console.log(`塔台通知 ${aircraft.getFlightNumber()}: 恶劣天气,准备紧急降落`);
    }

    const runway = this.runways.values().next().value;
    this.runways.delete(runway);
    
    console.log(`塔台批准 ${aircraft.getFlightNumber()} 在跑道 ${runway} 降落`);
    aircraft.approveLanding(runway);
    return true;
  }

  releaseRunway(runway) {
    this.runways.add(runway);
    console.log(`跑道 ${runway} 已释放`);
  }

  notifyWeatherChange(weather) {
    this.weather = weather;
    console.log(`塔台发布天气更新: ${weather}`);
    
    // 通知所有飞机天气变化
    for (const aircraft of this.aircrafts.values()) {
      aircraft.receiveWeatherUpdate(weather);
    }
  }

  getWeather() {
    return this.weather;
  }
}

// 抽象飞机类
class Aircraft {
  constructor(id, flightNumber) {
    this.id = id;
    this.flightNumber = flightNumber;
    this.controlTower = null;
    this.status = '停机坪'; // 停机坪、滑行、起飞、巡航、降落
  }

  getId() {
    return this.id;
  }

  getFlightNumber() {
    return this.flightNumber;
  }

  setControlTower(controlTower) {
    this.controlTower = controlTower;
  }

  requestTakeoff() {
    console.log(`飞机 ${this.flightNumber} 请求起飞`);
    if (this.controlTower) {
      return this.controlTower.requestTakeoff(this);
    }
    return false;
  }

  requestLanding() {
    console.log(`飞机 ${this.flightNumber} 请求降落`);
    if (this.controlTower) {
      return this.controlTower.requestLanding(this);
    }
    return false;
  }

  approveTakeoff(runway) {
    console.log(`飞机 ${this.flightNumber} 获得起飞许可,在跑道 ${runway} 起飞`);
    this.status = '起飞';
    // 模拟起飞后释放跑道
    setTimeout(() => {
      this.status = '巡航';
      this.controlTower.releaseRunway(runway);
    }, 1000);
  }

  approveLanding(runway) {
    console.log(`飞机 ${this.flightNumber} 获得降落许可,在跑道 ${runway} 降落`);
    this.status = '降落';
    // 模拟降落后释放跑道
    setTimeout(() => {
      this.status = '停机坪';
      this.controlTower.releaseRunway(runway);
    }, 1000);
  }

  receiveWeatherUpdate(weather) {
    console.log(`飞机 ${this.flightNumber} 收到天气更新: ${weather}`);
  }
}

// 具体客机类
class PassengerAircraft extends Aircraft {
  constructor(id, flightNumber, passengerCount) {
    super(id, flightNumber);
    this.passengerCount = passengerCount;
  }

  getPassengerCount() {
    return this.passengerCount;
  }
}

// 具体货机类
class CargoAircraft extends Aircraft {
  constructor(id, flightNumber, cargoWeight) {
    super(id, flightNumber);
    this.cargoWeight = cargoWeight;
  }

  getCargoWeight() {
    return this.cargoWeight;
  }
}

// 使用示例
const tower = new AirportControlTower();

// 添加跑道
tower.addRunway('01L');
tower.addRunway('01R');

// 创建飞机
const aircraft1 = new PassengerAircraft(1, 'CA101', 180);
const aircraft2 = new PassengerAircraft(2, 'MU202', 220);
const aircraft3 = new CargoAircraft(3, 'FedEx303', 50000);

// 注册飞机到塔台
tower.registerAircraft(aircraft1);
tower.registerAircraft(aircraft2);
tower.registerAircraft(aircraft3);

console.log('\n=== 飞机起降调度 ===');

// 飞机请求起飞
aircraft1.requestTakeoff();
aircraft2.requestTakeoff();

// 模拟等待
setTimeout(() => {
  aircraft3.requestTakeoff();
}, 1500);

// 模拟飞机请求降落
setTimeout(() => {
  console.log('\n=== 飞机返航降落 ===');
  aircraft1.requestLanding();
  aircraft3.requestLanding();
}, 3000);

// 模拟天气变化
setTimeout(() => {
  console.log('\n=== 天气变化 ===');
  tower.notifyWeatherChange('暴风雨');
}, 5000);

场景3:GUI组件交互系统

在图形用户界面中,多个组件之间需要协调交互,使用中介者模式可以减少组件间的直接依赖。

javascript
// 抽象界面中介者
class UIDialogMediator {
  registerComponent(component) {
    throw new Error('registerComponent method must be implemented');
  }

  notify(sender, event, data) {
    throw new Error('notify method must be implemented');
  }
}

// 具体界面中介者
class DialogMediator extends UIDialogMediator {
  constructor() {
    super();
    this.components = new Map();
  }

  registerComponent(component) {
    this.components.set(component.getName(), component);
    component.setMediator(this);
  }

  notify(sender, event, data) {
    switch (event) {
      case 'buttonClick':
        this.handleButtonClick(sender, data);
        break;
      case 'textInput':
        this.handleTextInput(sender, data);
        break;
      case 'checkboxToggle':
        this.handleCheckboxToggle(sender, data);
        break;
      case 'selectionChange':
        this.handleSelectionChange(sender, data);
        break;
      default:
        console.log(`未知事件: ${event}`);
    }
  }

  handleButtonClick(sender, data) {
    console.log(`按钮 ${sender.getName()} 被点击`);
    
    if (sender.getName() === 'submitBtn') {
      const usernameInput = this.components.get('usernameInput');
      const passwordInput = this.components.get('passwordInput');
      const termsCheckbox = this.components.get('termsCheckbox');
      
      if (!usernameInput.getValue()) {
        this.components.get('messageLabel').setText('请输入用户名');
        return;
      }
      
      if (!passwordInput.getValue()) {
        this.components.get('messageLabel').setText('请输入密码');
        return;
      }
      
      if (!termsCheckbox.isChecked()) {
        this.components.get('messageLabel').setText('请同意服务条款');
        return;
      }
      
      this.components.get('messageLabel').setText('登录成功!');
      console.log(`用户 ${usernameInput.getValue()} 登录成功`);
    }
    
    if (sender.getName() === 'resetBtn') {
      this.components.get('usernameInput').setValue('');
      this.components.get('passwordInput').setValue('');
      this.components.get('termsCheckbox').setChecked(false);
      this.components.get('countrySelect').setSelectedIndex(0);
      this.components.get('messageLabel').setText('请输入登录信息');
    }
  }

  handleTextInput(sender, data) {
    console.log(`文本输入框 ${sender.getName()} 内容改变: ${data}`);
    
    if (sender.getName() === 'usernameInput') {
      const submitBtn = this.components.get('submitBtn');
      submitBtn.setEnabled(data.length > 0);
    }
  }

  handleCheckboxToggle(sender, data) {
    console.log(`复选框 ${sender.getName()} 状态改变: ${data}`);
    
    if (sender.getName() === 'termsCheckbox') {
      const submitBtn = this.components.get('submitBtn');
      const termsCheckbox = this.components.get('termsCheckbox');
      submitBtn.setEnabled(termsCheckbox.isChecked());
    }
  }

  handleSelectionChange(sender, data) {
    console.log(`选择框 ${sender.getName()} 选择改变: ${data}`);
    
    if (sender.getName() === 'countrySelect') {
      const messageLabel = this.components.get('messageLabel');
      messageLabel.setText(`选择的国家: ${data}`);
    }
  }
}

// 抽象UI组件
class UIComponent {
  constructor(name) {
    this.name = name;
    this.mediator = null;
  }

  getName() {
    return this.name;
  }

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

  triggerEvent(event, data) {
    if (this.mediator) {
      this.mediator.notify(this, event, data);
    }
  }
}

// 标签组件
class Label extends UIComponent {
  constructor(name, text = '') {
    super(name);
    this.text = text;
  }

  setText(text) {
    this.text = text;
    console.log(`标签 ${this.name} 文本更新为: ${text}`);
  }

  getText() {
    return this.text;
  }
}

// 按钮组件
class Button extends UIComponent {
  constructor(name, text = '') {
    super(name);
    this.text = text;
    this.enabled = true;
  }

  setText(text) {
    this.text = text;
  }

  setEnabled(enabled) {
    this.enabled = enabled;
    console.log(`按钮 ${this.name} ${enabled ? '启用' : '禁用'}`);
  }

  isEnabled() {
    return this.enabled;
  }

  click() {
    if (this.enabled) {
      console.log(`按钮 ${this.name} 被点击`);
      this.triggerEvent('buttonClick', this.text);
    } else {
      console.log(`按钮 ${this.name} 已禁用,无法点击`);
    }
  }
}

// 文本输入框组件
class TextInput extends UIComponent {
  constructor(name) {
    super(name);
    this.value = '';
  }

  setValue(value) {
    this.value = value;
    this.triggerEvent('textInput', value);
  }

  getValue() {
    return this.value;
  }

  input(text) {
    this.setValue(text);
  }
}

// 复选框组件
class Checkbox extends UIComponent {
  constructor(name, text = '') {
    super(name);
    this.text = text;
    this.checked = false;
  }

  setText(text) {
    this.text = text;
  }

  setChecked(checked) {
    this.checked = checked;
    this.triggerEvent('checkboxToggle', checked);
  }

  isChecked() {
    return this.checked;
  }

  toggle() {
    this.setChecked(!this.checked);
  }
}

// 下拉选择框组件
class SelectBox extends UIComponent {
  constructor(name, options = []) {
    super(name);
    this.options = options;
    this.selectedIndex = 0;
  }

  setOptions(options) {
    this.options = options;
  }

  setSelectedIndex(index) {
    if (index >= 0 && index < this.options.length) {
      this.selectedIndex = index;
      this.triggerEvent('selectionChange', this.options[index]);
    }
  }

  getSelectedValue() {
    return this.options[this.selectedIndex];
  }

  select(value) {
    const index = this.options.indexOf(value);
    if (index !== -1) {
      this.setSelectedIndex(index);
    }
  }
}

// 使用示例
const mediator = new DialogMediator();

// 创建UI组件
const usernameLabel = new Label('usernameLabel', '用户名:');
const usernameInput = new TextInput('usernameInput');
const passwordLabel = new Label('passwordLabel', '密码:');
const passwordInput = new TextInput('passwordInput');
const countryLabel = new Label('countryLabel', '国家:');
const countrySelect = new SelectBox('countrySelect', ['中国', '美国', '英国', '日本']);
const termsCheckbox = new Checkbox('termsCheckbox', '我同意服务条款');
const submitBtn = new Button('submitBtn', '登录');
const resetBtn = new Button('resetBtn', '重置');
const messageLabel = new Label('messageLabel', '请输入登录信息');

// 注册组件到中介者
mediator.registerComponent(usernameLabel);
mediator.registerComponent(usernameInput);
mediator.registerComponent(passwordLabel);
mediator.registerComponent(passwordInput);
mediator.registerComponent(countryLabel);
mediator.registerComponent(countrySelect);
mediator.registerComponent(termsCheckbox);
mediator.registerComponent(submitBtn);
mediator.registerComponent(resetBtn);
mediator.registerComponent(messageLabel);

console.log('=== GUI组件交互演示 ===');

// 初始状态
console.log('\n--- 初始状态 ---');
submitBtn.click(); // 尝试提交空表单

// 输入用户名
console.log('\n--- 输入用户名 ---');
usernameInput.input('张三');

// 输入密码
console.log('\n--- 输入密码 ---');
passwordInput.input('123456');

// 同意条款
console.log('\n--- 同意服务条款 ---');
termsCheckbox.toggle();

// 选择国家
console.log('\n--- 选择国家 ---');
countrySelect.select('美国');

// 提交表单
console.log('\n--- 提交表单 ---');
submitBtn.click();

// 重置表单
console.log('\n--- 重置表单 ---');
resetBtn.click();

// 再次尝试提交(应该失败)
console.log('\n--- 再次提交(应该失败) ---');
submitBtn.click();

关键要点

  1. 降低耦合度:对象之间不需要直接引用,通过中介者进行交互,降低了系统的耦合度。

  2. 集中控制:将对象间的交互逻辑集中到中介者中,便于统一管理和维护。

  3. 符合迪米特法则:对象之间不需要直接通信,减少了对象之间的依赖关系。

  4. 易于扩展:增加新的同事类只需要修改中介者,而不需要修改现有的同事类。

与其他模式的关系

  • 与观察者模式结合:中介者可以使用观察者模式来通知同事对象状态变化。
  • 与单例模式结合:中介者通常以单例模式实现,确保系统中只有一个中介者实例。
  • 与外观模式区别:外观模式为子系统提供统一接口,而中介者模式协调多个对象间的交互。

优缺点

优点:

  1. 降低系统复杂性:将多对多关系转换为一对多关系,简化了对象间的交互。
  2. 提高系统可维护性:交互逻辑集中管理,便于维护和修改。
  3. 提高系统可扩展性:增加新的同事类相对容易,只需修改中介者即可。

缺点:

  1. 中介者复杂化:中介者可能变得非常复杂,难以维护。
  2. 违反单一职责原则:中介者可能承担过多的职责,职责不清晰。
  3. 性能问题:所有交互都通过中介者,可能影响系统性能。

中介者模式在需要协调多个对象交互的场景中非常有用,特别是在GUI系统、聊天系统、调度系统等场景中应用广泛。通过合理使用中介者模式,可以显著降低系统的复杂性和耦合度,提高系统的可维护性和可扩展性。