中介者模式实战指南:基于 Java 的多场景案例实现解析

一、生活场景引入(非技术角度)

场景1:机场塔台的智慧(空中交通管制)

想象一个繁忙的国际机场,有上百架飞机需要同时完成起降。如果没有塔台指挥会发生什么?

  • 混乱的原始状态

    每架飞机都试图直接与其他飞机通信:

    ✈️ 国航123:"东航456!我要降落3号跑道,别挡路!"

    ✈️ 东航456:"深航789!你离我太近了,快让开!"

    ✈️ 美航888:"谁看到我的停机位了?"

    ➡️ 无线电频道混乱不堪,飞行员需要记住所有飞机的动态

  • 塔台的出现

    🛕 塔台成为唯一协调者:

    ✈️ 所有飞机只与塔台通信

    🛑 塔台掌握全局信息(跑道占用、天气、航线)

    ✅ 简单清晰的指令:

    "国航123,允许降落2号跑道"

    "东航456,保持高度3000米盘旋等待"

关键启示 :塔台把 N×N 的通信网络 简化为 N×1 的星型结构


场景2:微信群聊的日常(消息中转中心)

假设公司有三个同事 Alice、Bob、Charlie 需要沟通:

  • 原始沟通方式 (无微信群):

    📱 Alice 需要分别给 Bob 和 Charlie 发同样的消息

    📧 Bob 想分享文件,要逐个发送给其他两人

    ❌ 问题:联系人管理复杂,新人加入需要所有人更新通讯录

  • 微信群解决方案

    🟩 创建"项目攻坚群"作为消息枢纽

    📨 任何人发消息到群里,自动同步给所有成员

    ➕ 新人加入只需扫码入群,无需逐个添加好友

flowchart LR A[Alice] -->|发消息| W[微信群] B[Bob] -->|发消息| W C[Charlie] -->|发消息| W W -->|推送| A W -->|推送| B W -->|推送| C

场景对比与模式提炼

场景 混乱点 解决方案 本质思想
机场调度 飞机间直接通信混乱 引入塔台中介 集中控制
微信沟通 点对点消息重复发送 创建群聊中介 信息中转
共性 多对象网状交互 统一协调中心 解耦对象间直接依赖

小白秒懂总结

当中介者不存时,对象间像没有指挥的交通路口;而中介者就像智能红绿灯,让所有车辆(对象)只需与红绿灯(中介)交互,不再需要关注其他车辆的具体动向。

二、模式解密(UML图)

用「群聊系统」理解类图结构

让我们通过微信群聊的场景,用大白话解释这个UML图的每个组成部分:

classDiagram class Mediator { <> +send(message: String, user: User): void } class ConcreteMediator { -users: List +register(user: User): void +send(message: String, user: User): void } class User { -name: String -mediator: Mediator +send(message: String): void +receive(message: String): void } Mediator <|.. ConcreteMediator User --> Mediator : uses ConcreteMediator o-- User : contains

🧩 核心组件拆解(对照微信群案例)

1. 中介者接口(Mediator)------ 群规制定者

classDiagram class Mediator { <> +send(message: String, user: User): void }
  • 角色:相当于微信的群功能规范
  • 作用
    • 定义消息转发规则(比如是否允许@所有人)
    • 声明核心方法 send,所有具体群类型必须实现该规则
  • 设计意义:通过接口约束,保证不同类型的群(工作群/亲友群)都遵守基本通信协议

2. 具体中介者(ConcreteMediator)------ 微信群实例

classDiagram class ConcreteMediator { -users: List +register(user: User): void +send(message: String, user: User): void }
  • 成员变量
    • users:保存群成员列表(相当于微信群成员列表)
  • 关键方法
    • register():入群操作(扫码加群时触发)
    • send():消息广播逻辑(决定消息是否全员可见或部分可见)
  • 实际场景
java 复制代码
// 示例:发送消息时排除发送者自己
public void send(String msg, User sender) {
    for (User u : users) {
        if(u != sender) u.receive(msg);
    }
}

3. 用户类(User)------ 群成员

classDiagram class User { -name: String -mediator: Mediator +send(message: String): void +receive(message: String): void }
  • 成员变量
    • mediator:持有群对象引用(用户必须加入群才能发言)
  • 关键行为
    • send():点击发送按钮时,把消息交给群处理
    • receive():接收群转发的消息(显示在聊天窗口)
  • 设计亮点 :用户无需知道其他成员的存在,只需和群交互

🔗 类关系详解(对照UML箭头)

1. 实现关系(空心三角箭头)

classDiagram Mediator <|.. ConcreteMediator
  • 类比:微信群(ConcreteMediator)必须遵守微信制定的群规范(Mediator)
  • 技术意义:面向接口编程,方便扩展不同类型的群(如禁言群、匿名群)

2. 使用依赖(普通箭头)

classDiagram User --> Mediator : uses
  • 现实映射:群成员通过微信APP(Mediator)发送消息
  • 代码体现
java 复制代码
public class User {
    private Mediator mediator; // 依赖中介者接口
    
    public void send(String msg) {
        mediator.send(msg, this); // 通过中介者发送
    }
}

3. 组合关系(实心菱形箭头)

classDiagram ConcreteMediator o-- User : contains
  • 现实意义:微信群对象持有所有群成员的引用
  • 代码体现
java 复制代码
public class ChatRoom implements Mediator {
    private List<User> users = new ArrayList<>(); // 组合用户对象
}
  • 生命周期:当微信群解散时,成员引用自动清除

💡 架构设计亮点总结

  1. 星型拓扑结构:所有交互都经过中心节点(对比直接通信的网状结构)
  2. 双重解耦
    • 用户不知道其他用户的存在
    • 中介者不关心具体业务逻辑(可通过不同ConcreteMediator实现不同策略)
  3. 开放扩展:要增加新群类型(如视频群),只需新建ConcreteMediator实现类

三、问题场景(没有中介者的困境)

用一个「相亲相爱一家人」群聊的悲剧故事

假设我们要实现家庭群聊功能,先看看没有中介者时会发生什么:

java 复制代码
// 问题代码示例:每个用户都要维护所有联系人
class User {
    private String name;
    private List<User> contacts = new ArrayList<>(); // 通讯录
    
    public void addContact(User user) {
        contacts.add(user);
    }

    public void sendDirectMessage(String msg, User to) {
        System.out.println(name + " 私聊 " + to.name + ": " + msg);
        to.receiveDirectMessage(msg, this);
    }
    
    // 群发消息的灾难性实现
    public void broadcast(String msg) {
        for (User contact : contacts) {
            sendDirectMessage(msg, contact);
        }
    }
}

💥 问题集中爆发时刻(代码演示)

场景:妈妈想把菜谱分享给全家人

java 复制代码
public static void main(String[] args) {
    User 妈妈 = new User("妈妈");
    User 爸爸 = new User("爸爸");
    User 我 = new User("我");
    User 爷爷 = new User("爷爷");

    // 每个用户都要添加其他联系人
    妈妈.addContact(爸爸);
    妈妈.addContact(我);
    妈妈.addContact(爷爷);
    
    爸爸.addContact(妈妈);
    爸爸.addContact(我);
    // 爸爸忘记添加爷爷...
    
    // 妈妈发送群发消息
    妈妈.broadcast("新学的红烧肉做法!");
}

运行结果

arduino 复制代码
妈妈 私聊 爸爸: 新学的红烧肉做法!
妈妈 私聊 我: 新学的红烧肉做法!
妈妈 私聊 爷爷: 新学的红烧肉做法!
爸爸 收到消息: 新学的红烧肉做法! // 正常
我 收到消息: 新学的红烧肉做法!   // 正常
爷爷 收到消息: 新学的红烧肉做法! // 正常

似乎正常?但隐藏着致命问题...


🚨 四大痛点分析

  1. 维护成本爆炸

    每新增一个家庭成员(如奶奶),所有人都要修改通讯录:

    java 复制代码
    妈妈.addContact(奶奶);
    爸爸.addContact(奶奶);
    我.addContact(奶奶);
    爷爷.addContact(奶奶);
    // 漏掉一个就会导致奶奶收不到消息
  2. 消息不一致风险

    当爸爸忘记添加爷爷的联系方式时:

    java 复制代码
    // 爸爸想通知家人加班
    爸爸.broadcast("今晚加班不回家吃饭");
    // 爷爷永远收不到这个消息
  3. 功能扩展困难

    若要实现@指定人功能:

    java 复制代码
    public void sendTo(String msg, List<User> targets) {
        // 需要判断每个target是否在自己的通讯录中
    }

    每个用户都要实现复杂的目标过滤逻辑

  4. 对象关系混乱

    graph TD 妈妈 --> 爸爸 妈妈 --> 我 妈妈 --> 爷爷 爸爸 --> 妈妈 爸爸 --> 我 我 --> 妈妈 我 --> 爸爸 我 --> 爷爷 %% 形成复杂的网状结构

💡 关键问题总结

问题类型 具体表现 类比现实
强耦合 用户直接持有其他用户的引用 每个家庭成员的手机里存着所有人的电话号码
高维护成本 添加/删除用户需要联动修改多处 家族有新成员出生,所有亲戚的通讯录都要更新
低扩展性 新增功能(如消息撤回)需修改所有用户类 每家都要自建信号塔才能通信
单点故障 某个用户出错会影响整个通信链路 某人的手机没电导致全家失联

四、解决方案(引入中介者)

用「小区快递柜」类比消息中转

让我们通过更生活化的例子,理解这个架构图的工作流程:

flowchart TD A[UserA] -->|放入快递| M[快递柜] B[UserB] -->|放入快递| M C[UserC] -->|放入快递| M M -->|取出快递| A M -->|取出快递| B M -->|取出快递| C

🛠️ 新架构核心原理(三步流程)

步骤1:用户发送消息到中介者

java 复制代码
// User类中的发送方法
public void send(String msg) {
    System.out.println(name + " 发出消息:" + msg);
    mediator.sendMessage(msg, this); // 把消息交给快递柜
}
  • 类比:把包裹放入快递柜格口
  • 技术意义:用户不再需要知道接收者是谁

步骤2:中介者处理消息

java 复制代码
// ChatRoom中介者的消息处理
public void sendMessage(String msg, User sender) {
    System.out.println("--- 中介者开始派发消息 ---");
    for (User user : users) {
        if(user != sender) {  // 不送回发件人
            user.receive(msg); // 将包裹放入收件人柜格
        }
    }
}
  • 过滤逻辑:自动屏蔽发送者自身(类似快递柜不会把包裹存回寄件人柜格)
  • 路由控制:集中处理消息分发策略(可在此扩展消息加密、敏感词过滤等功能)

步骤3:用户接收消息

java 复制代码
// User类中的接收方法
public void receive(String msg) {
    System.out.println(name + " 收到消息:" + msg);
}
  • 类比:从快递柜取件
  • 设计优势:接收方无需关心消息来源,专注处理内容

🔄 完整交互流程演示

场景:技术讨论群

java 复制代码
public static void main(String[] args) {
    // 创建中介者 - 相当于新建微信群
    ChatMediator mediator = new ChatRoom();
    
    // 创建用户并入群
    User frontend = new User("前端开发", mediator);
    User backend = new User("后端开发", mediator);
    User pm = new User("产品经理", mediator);

    // 发送消息
    pm.send("需求文档已更新");
    backend.send("接口已开发完成");
}

执行过程可视化

sequenceDiagram participant PM as 产品经理 participant Mediator as 微信群 participant FE as 前端开发 participant BE as 后端开发 PM->>Mediator: 发送"需求文档已更新" Mediator->>FE: 转发消息 Mediator->>BE: 转发消息 BE->>Mediator: 发送"接口已开发完成" Mediator->>PM: 转发消息 Mediator->>FE: 转发消息

控制台输出

复制代码
前端开发 收到消息:需求文档已更新
后端开发 收到消息:需求文档已更新
后端开发 发出消息:接口已开发完成
产品经理 收到消息:接口已开发完成
前端开发 收到消息:接口已开发完成

🆚 新旧方案对比

代码复杂度对比

java 复制代码
// 旧方案(用户直接交互)
userA.sendToAll("消息"); // 每个用户要自己遍历联系人

// 新方案(通过中介者)
userA.send("消息"); // 只需调用中介者的统一接口

对象关系变化

graph LR 旧模式((旧模式)) --> 网状结构["A ↔ B ↔ C ↔ D"] 新模式((新模式)) --> 星型结构["A → M ← B
C → M ← D"]

扩展性对比

功能需求 旧方案改动点 新方案改动点
添加消息已读回执 修改所有User类 只需修改ChatRoom类
实现@指定人功能 每个用户维护联系人关系 中介者统一管理用户列表
添加敏感词过滤 每个发送方法都要添加过滤逻辑 在中介者中统一处理

💡 架构升级收益总结

  1. 联系人零维护:用户不再需要持有其他对象的引用
  2. 集中管控点:所有消息处理逻辑收敛到中介者
  3. 扩展无侵入:新增功能只需修改中介者,不影响现有用户类
  4. 关系可视化:通过中介者的日志可以清晰追踪所有交互记录
  5. 资源节约:100个用户的群聊,消息发送次数从100×99=9900次减少到100次

现实映射:就像从原始部落的点对点物物交换,进化到现代物流中心的分发体系,中介者模式让系统交互进入了工业化时代。

五、完整代码实现(分步骤讲解)

1. 中介者接口:定义通信规则(相当于微信群协议)

java 复制代码
/**
 * 中介者接口:声明群聊的基本功能
 * 相当于微信群的标准化功能定义
 */
public interface ChatMediator {
    /**
     * 发送消息给其他人
     * @param msg 消息内容
     * @param sender 发送者(用于排除自己)
     */
    void sendMessage(String msg, User sender);

    /**
     * 添加用户到群聊
     * @param user 要加入的用户
     */
    void addUser(User user);
}

代码要点

  • 接口只定义两个核心功能:消息发送和用户添加
  • 类似微信群的基础协议,所有具体群类型必须遵守
  • 参数sender用于识别消息来源,实现不发送给自己的逻辑

2. 具体中介者:聊天室实现(完整版)

java 复制代码
import java.util.ArrayList;
import java.util.List;

/**
 * 具体中介者:微信群的具体实现
 * 相当于一个工作群,自动排除发送者自身
 */
public class ChatRoom implements ChatMediator {
    private List<User> users = new ArrayList<>(); // 群成员列表

    @Override
    public void addUser(User user) {
        // 入群时自动添加
        users.add(user);
        System.out.println(user.getName() + " 加入了群聊");
    }

    @Override
    public void sendMessage(String msg, User sender) {
        System.out.println("\n【群消息中转】来自 " + sender.getName());
        for (User user : users) {
            // 关键逻辑:不将消息发回给发送者
            if (user != sender) {
                user.receive(msg); // 调用接收者的接收方法
            }
        }
    }
}

代码升级说明

  1. 增加入群提示(现实场景可扩展为入群欢迎语)
  2. 添加详细日志,显示消息中转过程
  3. 使用getName()方法代替直接访问字段(面向对象封装原则)
  4. 导入必要的包(实际开发时容易忽略的细节)

3. 用户类:与中介者交互(增强版)

java 复制代码
/**
 * 用户类:不再维护任何联系人信息
 * 相当于只关注自己与微信群的交互
 */
public class User {
    private String name;
    private ChatMediator mediator;

    public User(String name, ChatMediator mediator) {
        this.name = name;
        this.mediator = mediator;
        // 注册到群聊(相当于扫码加群)
        mediator.addUser(this);
    }

    public String getName() {
        return name;
    }

    public void send(String msg) {
        System.out.println("\n[" + name + "] 点击发送按钮");
        mediator.sendMessage(msg, this); // 只与中介者交互
    }

    public void receive(String msg) {
        System.out.println("   " + name + " 的聊天窗口显示 <- " + msg);
    }
}

新增特性

  1. 封装name字段并提供访问方法
  2. 构造时自动注册到中介者(避免忘记添加用户)
  3. 模拟真实操作流程(点击发送按钮的日志)
  4. 更逼真的接收效果显示

4. 客户端使用(带运行结果演示)

java 复制代码
/**
 * 演示:家庭群聊场景
 */
public class FamilyChatDemo {
    public static void main(String[] args) {
        // 创建微信群(中介者)
        ChatMediator familyWeChatGroup = new ChatRoom();

        // 创建家庭成员(入群操作在构造函数自动完成)
        User mother = new User("妈妈", familyWeChatGroup);
        User father = new User("爸爸", familyWeChatGroup);
        User son = new User("小明", familyWeChatGroup);
        
        // 奶奶稍后入群
        User grandma = new User("奶奶", familyWeChatGroup);

        System.out.println("\n======== 开始群聊 ========");
        mother.send("超市鸡蛋打折,要不要多买点?");
        grandma.send("我手机怎么发不了图片啊?");
    }
}

运行结果

css 复制代码
妈妈 加入了群聊
爸爸 加入了群聊
小明 加入了群聊
奶奶 加入了群聊

======== 开始群聊 ========

[妈妈] 点击发送按钮

【群消息中转】来自 妈妈
   爸爸 的聊天窗口显示 <- 超市鸡蛋打折,要不要多买点?
   小明 的聊天窗口显示 <- 超市鸡蛋打折,要不要多买点?
   奶奶 的聊天窗口显示 <- 超市鸡蛋打折,要不要多买点?

[奶奶] 点击发送按钮

【群消息中转】来自 奶奶
   妈妈 的聊天窗口显示 <- 我手机怎么发不了图片啊?
   爸爸 的聊天窗口显示 <- 我手机怎么发不了图片啊?
   小明 的聊天窗口显示 <- 我手机怎么发不了图片啊?

代码设计亮点解读

  1. 自动注册机制

    java 复制代码
    // 用户在创建时自动加入群聊
    public User(String name, ChatMediator mediator) {
        mediator.addUser(this);
    }
    • 避免忘记将用户添加到群聊
    • 类似微信扫码加群的体验
  2. 双向解耦设计

    graph LR User-->Mediator Mediator-->User
    • 用户不知道其他用户的存在
    • 中介者只负责转发,不处理业务逻辑
  3. 开闭原则实践

    java 复制代码
    public class VIPChatRoom implements ChatMediator { 
        // 可扩展为VIP专属群
    }
    • 新增群类型无需修改现有代码
    • 可自由扩展消息过滤、@指定人等功能

可扩展性演示:添加敏感词过滤

java 复制代码
/**
 * 升级版中介者:带敏感词过滤的家长群
 */
public class ParentChatRoom extends ChatRoom {
    private static final List<String> BANNED_WORDS = 
        List.of("打架", "逃课", "早恋");

    @Override
    public void sendMessage(String msg, User sender) {
        // 过滤敏感词
        String filteredMsg = filter(msg);
        super.sendMessage(filteredMsg, sender);
    }

    private String filter(String original) {
        String result = original;
        for (String word : BANNED_WORDS) {
            result = result.replace(word, "**");
        }
        return result;
    }
}

// 使用示例
ChatMediator parentGroup = new ParentChatRoom();
new User("张老师", parentGroup).send("最近发现有些同学想早恋!");
// 输出:最近发现有些同学想**!

扩展提示

  • 只需新建中介者子类即可实现功能升级
  • 完全不影响原有User类的实现
  • 符合开闭原则(对扩展开放,对修改关闭)

六、模式优势总结(对比表格+实战解读)

用「微信群聊」案例理解优势差异

指标 无中介者(私聊模式) 有中介者(群聊模式) 实战影响
对象耦合度 每个用户持有其他所有用户的引用 用户只持有群聊中介者引用 修改用户类不会影响消息系统,新增功能只需修改中介者类
新增用户成本 需要修改所有现有用户的联系人列表 只需调用mediator.addUser() 100人群新增成员时,工作量从修改100个用户类减少到1次方法调用
消息控制 每个用户自己实现发送逻辑 在中介者中统一处理过滤、路由、日志 添加敏感词过滤功能只需修改中介者类,无需改动每个用户的发送方法
通信复杂度 N个用户需要维护N×(N-1)个通信通道 只需维护N个用户到1个中介者的通道 用户数从10增加到100时,通信通道数从9900减少到100,降低两个数量级
错误排查 需要跟踪多个对象间的交互 所有交互记录在中介者日志中 快速定位消息丢失问题,类似微信群的"聊天记录"功能

七、实际应用场景(含代码片段)

场景1:GUI组件交互 - 登录表单验证

java 复制代码
// 中介者接口
interface FormMediator {
    void onUsernameChange(String text);
    void onPasswordChange(String text);
    void registerComponent(Component component);
}

// 具体中介者
class LoginFormMediator implements FormMediator {
    private JTextField username;
    private JPasswordField password;
    private JButton submitButton;

    @Override
    public void onUsernameChange(String text) {
        boolean valid = !text.isEmpty();
        submitButton.setEnabled(valid && password.getPassword().length > 6);
    }

    @Override
    public void registerComponent(Component component) {
        if (component instanceof JTextField) {
            username = (JTextField)component;
        } else if (component instanceof JPasswordField) {
            password = (JPasswordField)component;
        }
    }
}

// 使用示例
FormMediator mediator = new LoginFormMediator();
JTextField usernameField = new JTextField();
usernameField.addActionListener(e -> 
    mediator.onUsernameChange(usernameField.getText()));
// 其他组件类似注册...

工作原理

flowchart TD A[用户名输入框] -->|内容变更| M[表单中介者] B[密码输入框] -->|内容变更| M M -->|更新状态| C[提交按钮]

场景2:微服务API网关 - 请求路由

java 复制代码
// 简化版网关中介者
public class ApiGateway {
    private Map<String, Service> services = new HashMap<>();
    
    public void registerService(String path, Service service) {
        services.put(path, service);
    }
    
    public Response handleRequest(Request request) {
        Service service = services.get(request.getPath());
        if (service != null) {
            // 统一处理鉴权、限流
            if (checkAuth(request)) {
                return service.execute(request);
            }
            return new Response(403, "Forbidden");
        }
        return new Response(404, "Not Found");
    }
}

// 客户端调用
ApiGateway gateway = new ApiGateway();
gateway.registerService("/order", new OrderService());
gateway.registerService("/payment", new PaymentService());

Response res = gateway.handleRequest(
    new Request("/order?item=book", "GET"));

架构优势

  • 服务提供者无需关心身份验证等横切关注点
  • 新增服务只需注册,客户端调用方式不变

场景3:游戏技能系统 - 技能连锁触发

java 复制代码
class SkillMediator {
    private List<Player> players = new ArrayList<>();
    
    public void castSkill(Player caster, Skill skill) {
        // 1. 计算伤害范围
        // 2. 触发队友增益BUFF
        // 3. 触发敌人减益效果
        players.stream()
            .filter(p -> inRange(p, caster))
            .forEach(p -> {
                if (isAlly(p, caster)) {
                    p.addBuff(skill.getBuff());
                } else {
                    p.addDebuff(skill.getDebuff());
                }
            });
    }
}

// 玩家释放技能时
public class Player {
    private SkillMediator mediator;
    
    public void useSkill(Skill skill) {
        mediator.castSkill(this, skill);
    }
}

设计亮点

  • 技能效果计算逻辑集中处理
  • 新增技能类型不影响玩家类

场景4:工作流引擎 - 订单状态机

java 复制代码
class OrderWorkflowMediator {
    private Map<OrderState, List<StateHandler>> handlers = new HashMap<>();
    
    public void transition(Order order, OrderState newState) {
        // 1. 检查状态转换是否合法
        // 2. 触发关联操作(发邮件、通知仓库等)
        // 3. 更新状态
        handlers.getOrDefault(newState, Collections.emptyList())
            .forEach(handler -> handler.handle(order));
    }
}

// 使用示例
mediator.transition(order, OrderState.SHIPPED);

业务流程

stateDiagram-v2 [*] --> PENDING PENDING --> PAID: 支付完成 PAID --> SHIPPED: 发货 SHIPPED --> DELIVERED: 签收 SHIPPED --> RETURNED: 退货 DELIVERED --> [*]

八、最佳实践建议(避坑指南)

  1. 合理控制中介者规模

    • 当一个中介者类超过1000行代码时,考虑拆分为多个专项中介者
    • 示例:电商系统可拆分为OrderMediatorPaymentMediator
  2. 避免上帝对象

    java 复制代码
    // 错误示例:中介者包办一切
    class BadMediator {
        void handleUser() { /* 用户管理 */ }
        void calcPrice() { /* 价格计算 */ }
        void generateReport() { /* 报表生成 */ }
    }
    
    // 正确做法:职责分离
    class OrderMediator { /* 仅处理订单相关协调 */ }
    class ReportMediator { /* 仅处理报表生成协调 */ }
  3. 与观察者模式结合使用

    java 复制代码
    // 中介者内部使用观察者模式
    class SmartMediator {
        private List<Consumer<Event>> listeners = new ArrayList<>();
        
        public void addListener(Consumer<Event> listener) {
            listeners.add(listener);
        }
        
        private void notifyListeners(Event event) {
            listeners.forEach(l -> l.accept(event));
        }
    }
  4. 性能优化策略

    • 对高频交互场景使用缓存(如游戏技能中介者缓存伤害计算)
    • 采用异步处理(如订单流程中介者使用消息队列)

九、延伸思考题(附解决方案思路)

Q1:中介者模式与观察者模式的异同?

场景对比分析

假设要实现一个新用户入群通知功能:

java 复制代码
// 用观察者模式实现
class ChatGroup {
    private List<Observer> observers = new ArrayList<>();
    
    public void addObserver(Observer o) {
        observers.add(o);
    }
    
    private void notifyNewUser(User user) {
        observers.forEach(o -> o.update("新用户加入:" + user));
    }
}

// 用中介者模式实现
class ChatMediatorImpl {
    private List<User> users = new ArrayList<>();
    
    public void addUser(User user) {
        users.add(user);
        sendSystemMsg("欢迎 " + user + " 加入群聊");
    }
}

核心差异总结

维度 观察者模式 中介者模式
交互方向 单向(主题 → 观察者) 双向(同事 ↔ 中介者)
关注点 状态变化通知 对象间交互协调
耦合度 观察者依赖主题 同事类只依赖中介者
典型场景 事件通知(如按钮点击) 复杂交互协调(如聊天路由)
控制权 由观察者决定是否响应 由中介者控制消息流向

组合使用案例

java 复制代码
// 在中介者内部使用观察者
class SmartMediator implements Mediator {
    private List<Consumer<String>> listeners = new ArrayList<>();
    
    public void addListener(Consumer<String> listener) {
        listeners.add(listener);
    }
    
    public void send(String msg) {
        // 先处理消息逻辑
        String processedMsg = process(msg);
        // 再通知观察者
        listeners.forEach(l -> l.accept(processedMsg));
    }
}

Q2:如何在中介者中实现定向消息发送?

场景需求:在群聊中实现@指定用户功能

解决方案

  1. 消息协议设计

    定义消息格式 @用户名 消息内容

  2. 修改中介者逻辑

java 复制代码
public class AdvancedChatRoom extends ChatRoom {
    @Override
    public void sendMessage(String msg, User sender) {
        if (msg.startsWith("@")) {
            // 定向消息处理
            String[] parts = msg.split(" ", 2);
            String targetName = parts[0].substring(1);
            String content = parts.length > 1 ? parts[1] : "";
            
            users.stream()
                 .filter(u -> u.getName().equals(targetName))
                 .findFirst()
                 .ifPresent(u -> u.receive("[私信] " + content));
        } else {
            // 广播消息
            super.sendMessage(msg, sender);
        }
    }
}
  1. 客户端使用
java 复制代码
User bob = new User("Bob", mediator);
new User("Alice", mediator).send("@Bob 今晚一起吃饭吗?");

执行效果

css 复制代码
Bob 收到消息: [私信] 今晚一起吃饭吗?

扩展方案对比

方案 优点 缺点
消息协议解析 兼容现有接口 需要解析字符串
新增定向发送API 接口明确 需要修改所有发送逻辑
消息对象封装 类型安全 增加系统复杂性

Q3:怎样防止中介者出现性能瓶颈?

典型瓶颈场景

  • 万人聊天室的消息广播
  • 高频交易订单的状态流转
  • 大型MMO游戏的技能结算

优化策略工具箱

  1. 异步处理
java 复制代码
class AsyncMediator {
    private Executor executor = Executors.newFixedThreadPool(4);
    private BlockingQueue<Message> queue = new LinkedBlockingQueue<>();
    
    public void sendAsync(Message msg) {
        queue.offer(msg);
        executor.execute(() -> processQueue());
    }
    
    private void processQueue() {
        while (!queue.isEmpty()) {
            Message msg = queue.poll();
            // 实际处理逻辑
        }
    }
}

适用场景:允许短暂延迟的消息系统

  1. 分片处理
graph TD M[主中介者] --> S1[分片1] M --> S2[分片2] M --> S3[分片3]

实现示例

java 复制代码
class ShardedChatMediator {
    private List<ChatMediator> shards = new ArrayList<>();
    
    public ShardedChatMediator(int shardCount) {
        for(int i=0; i<shardCount; i++) {
            shards.add(new ChatRoom());
        }
    }
    
    public void send(String msg, User user) {
        int shardIndex = user.hashCode() % shards.size();
        shards.get(shardIndex).send(msg, user);
    }
}
  1. 缓存优化
java 复制代码
class CachedMediator {
    private Map<String, CacheEntry> cache = new ConcurrentHashMap<>();
    
    public void process(Request req) {
        String key = generateCacheKey(req);
        CacheEntry entry = cache.get(key);
        if (entry == null || entry.isExpired()) {
            entry = computeResult(req);
            cache.put(key, entry);
        }
        return entry.getValue();
    }
}

适用场景:频繁重复计算的场景(如游戏伤害计算)

  1. 批量处理
java 复制代码
class BatchMediator {
    private List<Message> batch = new ArrayList<>();
    
    public synchronized void send(Message msg) {
        batch.add(msg);
        if (batch.size() >= 100) {
            flushBatch();
        }
    }
    
    private void flushBatch() {
        // 批量写入数据库或发送
    }
}

性能优化决策树

graph TD A[遇到性能问题?] --> B{消息量级} B -->|小于1k/s| C[单线程+异步] B -->|1k-10k/s| D[多线程+队列] B -->|大于10k/s| E[分片+集群] C --> F[使用内存队列] D --> G[线程池优化] E --> H[分布式中介者]

总结建议

  1. 优先考虑异步非阻塞方案
  2. 根据业务特点选择分片策略(按用户ID哈希、按业务类型等)
  3. 对读多写少场景使用缓存+过期策略
  4. 监控中介者的消息处理延迟和队列长度
  5. 在微服务架构中可考虑将中介者部署为独立服务
相关推荐
Victor3565 分钟前
Redis(14)Redis的列表(List)类型有哪些常用命令?
后端
Victor3565 分钟前
Redis(15)Redis的集合(Set)类型有哪些常用命令?
后端
卷福同学6 分钟前
来上海三个月,我在马路边上遇到了阿里前同事...
java·后端
bobz9659 小时前
小语言模型是真正的未来
后端
DevYK9 小时前
企业级 Agent 开发实战(一) LangGraph 快速入门
后端·llm·agent
一只叫煤球的猫10 小时前
🕰 一个案例带你彻底搞懂延迟双删
java·后端·面试
冒泡的肥皂10 小时前
MVCC初学demo(一
数据库·后端·mysql
颜如玉11 小时前
ElasticSearch关键参数备忘
后端·elasticsearch·搜索引擎
卡拉叽里呱啦12 小时前
缓存-变更事件捕捉、更新策略、本地缓存和热key问题
分布式·后端·缓存
David爱编程12 小时前
线程调度策略详解:时间片轮转 vs 优先级机制,面试常考!
java·后端