中介者模式:对象间解耦、集中控制、实战:聊天室系统

说实话,中介者模式是我在重构老系统时用得最多的模式之一。为什么?因为很多项目写着写着,对象之间的引用关系就乱成一锅粥了。你想想看,A 调 B,B 调 C,C 又调 A……这种网状结构,改一个地方,牵一发动全身。

中介者模式就是来解决这个问题的。它把多对多的交互,转成了一对多的控制。说白了,就是找个“中间人”来统一调度。

什么是中介者模式?

中介者模式(Mediator Pattern)定义了一个中介对象,用来封装一组对象之间的交互。这些对象不需要直接引用对方,只需要跟中介者打交道就行。

我个人的理解是:它就像公司里的项目经理。开发人员不用直接去找产品经理吵架,也不用直接去跟测试对线。所有需求、问题、变更,都通过项目经理来协调。项目经理就是那个中介者。

核心思想: 将对象间的多对多关系,转化为中介者与各对象的一对多关系。

为什么需要中介者模式?

我在项目中遇到过这样一个场景:一个电商后台系统,订单模块、库存模块、物流模块、通知模块之间互相调用。订单创建后要通知库存扣减,库存扣完要通知物流发货,物流发货后要通知用户……

每个模块都持有其他模块的引用。后来需求变了,要加一个优惠券模块。好嘛,所有相关模块都得改一遍。这就是典型的“网状结构”带来的维护噩梦。

中介者模式能带来两个明显的好处:

  • 解耦: 各对象不再直接引用,只依赖中介者
  • 集中控制: 交互逻辑集中在中介者里,改一处就行

模式结构

中介者模式涉及以下几个角色:

角色 说明
Mediator(抽象中介者) 定义通信接口,声明消息发送方法
ConcreteMediator(具体中介者) 实现协调逻辑,维护各同事对象的引用
Colleague(同事类) 每个同事对象都持有中介者的引用,通过中介者与其他同事通信

嗯,这里要注意:同事类之间不能直接调用。所有通信必须走中介者。这是原则,破了这个原则,中介者模式就白搭了。

SVG 结构图:中介者模式核心逻辑

中介者 (Mediator) 同事A (Colleague) 同事B (Colleague) 同事C (Colleague) 发送消息 接收消息/回调 所有同事只与中介者通信,不直接交互 多对多 → 一对多

实战:聊天室系统

聊天室是中介者模式的经典案例。每个用户(同事)发送消息时,不是直接发给其他用户,而是发给聊天室服务器(中介者),由服务器转发给所有人。

我们来实现一个简化版。

1. 定义抽象中介者

public abstract class ChatMediator {
    public abstract void sendMessage(String message, User user);
    public abstract void addUser(User user);
}

2. 定义同事类(用户)

public abstract class User {
    protected String name;
    protected ChatMediator mediator;

    public User(String name, ChatMediator mediator) {
        this.name = name;
        this.mediator = mediator;
    }

    public abstract void send(String message);
    public abstract void receive(String message);
}

3. 实现具体中介者

public class ChatRoom extends ChatMediator {
    private List<User> users = new ArrayList<>();

    @Override
    public void addUser(User user) {
        users.add(user);
    }

    @Override
    public void sendMessage(String message, User sender) {
        for (User user : users) {
            // 消息不发给发送者自己
            if (user != sender) {
                user.receive(sender.name + ": " + message);
            }
        }
    }
}

4. 实现具体用户

public class ChatUser extends User {
    public ChatUser(String name, ChatMediator mediator) {
        super(name, mediator);
    }

    @Override
    public void send(String message) {
        System.out.println(this.name + " 发送消息: " + message);
        mediator.sendMessage(message, this);
    }

    @Override
    public void receive(String message) {
        System.out.println(this.name + " 收到消息: " + message);
    }
}

5. 客户端使用

public class Client {
    public static void main(String[] args) {
        ChatMediator chatRoom = new ChatRoom();

        User alice = new ChatUser("Alice", chatRoom);
        User bob = new ChatUser("Bob", chatRoom);
        User charlie = new ChatUser("Charlie", chatRoom);

        chatRoom.addUser(alice);
        chatRoom.addUser(bob);
        chatRoom.addUser(charlie);

        alice.send("大家好!");
        bob.send("嗨,Alice!");
    }
}

运行结果:

Alice 发送消息: 大家好!
Bob 收到消息: Alice: 大家好!
Charlie 收到消息: Alice: 大家好!
Bob 发送消息: 嗨,Alice!
Alice 收到消息: Bob: 嗨,Alice!
Charlie 收到消息: Bob: 嗨,Alice!

个人经验: 我在做即时通讯项目时,一开始没考虑中介者模式,每个客户端直接维护其他客户端的连接列表。后来用户量一上来,连接管理、消息转发、异常处理全混在一起,代码根本没法维护。重构后用了类似上面的中介者结构,问题迎刃而解。

中介者模式的优缺点

优点 缺点
减少类之间的直接耦合 中介者可能变得庞大复杂
将多对多简化为一对多 中介者出问题会影响所有同事
交互逻辑集中,便于维护 同事类数量多时,中介者逻辑膨胀
新增同事类只需修改中介者 可能引入性能瓶颈

避坑指南: 我曾经在一个项目中把中介者写成了“上帝对象”——所有业务逻辑都往里塞。结果中介者类超过 3000 行,改一个功能要翻半天代码。记住:中介者只负责协调,不负责业务处理。业务逻辑应该留在各自的同事类里。

什么时候用中介者模式?

我个人习惯在以下几种场景考虑使用:

  • 一组对象之间通信方式复杂,形成了网状结构
  • 想复用某个对象,但它依赖太多其他对象
  • 交互逻辑经常变化,希望集中管理

但也要注意,如果对象之间的交互很简单,比如就两三个对象互相调用,那硬套中介者模式反而画蛇添足。设计模式是工具,不是目的。

小结

中介者模式的核心就一句话:让对象之间不直接说话,都通过中介者传话。它把混乱的网状结构,变成了清晰的一对多结构。

聊天室系统只是其中一个例子。你想想看,GUI 里的对话框、MVC 中的 Controller、消息队列中的 Broker……本质上都是中介者模式的变体。理解了这一点,你会发现这个模式其实无处不在。

嗯,最后提醒一句:别把中介者写成大泥球。保持它的职责单一,只做协调,不做业务。这样你的代码才能既优雅又健壮。


公众号:蓝海资料掘金营,微信deep3321