中介者模式实战指南:基于 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. 在微服务架构中可考虑将中介者部署为独立服务
相关推荐
IsPrisoner23 分钟前
Go语言安装proto并且使用gRPC服务(2025最新WINDOWS系统)
开发语言·后端·golang
tan180°2 小时前
Linux进程信号处理(26)
linux·c++·vscode·后端·信号处理
有梦想的攻城狮2 小时前
spring中的@MapperScan注解详解
java·后端·spring·mapperscan
柚个朵朵3 小时前
Spring的Validation,这是一套基于注解的权限校验框架
java·后端·spring
Asus.Blogs4 小时前
为什么go语言中返回的指针类型,不需要用*取值(解引用),就可以直接赋值呢?
开发语言·后端·golang
C_V_Better4 小时前
Java Spring Boot 控制器中处理用户数据详解
java·开发语言·spring boot·后端·spring
胡子洲4 小时前
Spring Boot 应用中实现基本的 SSE 功能
java·spring boot·后端
贰拾wan5 小时前
【Java-EE进阶】SpringBoot针对某个IP限流问题
java·spring boot·后端·idea
Paran-ia5 小时前
【2025版】Spring Boot面试题
java·spring boot·后端
sufu10656 小时前
SpringAI更新:废弃tools方法、正式支持DeepSeek!
人工智能·后端