中介者模式:对象交互解耦的艺术

说实话,我在刚做架构设计那几年,最头疼的就是对象之间的通信问题。你想想看,一个系统里十几个类互相调用,A调B,B调C,C又调A……最后代码改一处,牵一发而动全身。这种场景,我猜你也遇到过。

中介者模式(Mediator Pattern)就是专门解决这个问题的。它把对象之间的网状交互,变成了星型交互——所有对象只跟中介者说话,中介者负责转发消息。说白了,就是给这群对象请了个“管家”。

为什么需要中介者?

先看一个反面教材。假设你写一个聊天室,没有中介者的话,每个用户都要知道其他所有用户的引用:

// 糟糕的设计:用户之间直接通信
public class User {
    private String name;
    private List<User> friends;
    
    public void sendMessage(String msg, User to) {
        to.receive(msg, this);
    }
    
    public void receive(String msg, User from) {
        System.out.println(from.name + " 对你说: " + msg);
    }
}

这有什么问题?耦合太紧。每个用户都要维护一个好友列表,新增一个用户,所有相关用户都得更新。更可怕的是,如果我想加个“群发”功能,每个用户都得改代码。

我在一个即时通讯项目里就吃过这个亏。当时团队图省事,让客户端之间直接通信。结果上线后,加一个“消息已读”功能,改了十几个文件……后来我果断重构,引入了中介者模式。

中介者模式的核心结构

先看一张图,帮你快速理解:

中介者 同事A 同事B 同事C 同事D 所有同事只与中介者通信 中介者负责协调和转发消息 发送消息 接收消息

这张图很直观:同事对象(Colleague)只跟中介者(Mediator)交互。中介者知道所有同事,但同事之间互不知道。这就是解耦的关键。

Java实现:聊天室案例

咱们用聊天室来实战。先定义中介者接口:

// 中介者接口
public interface ChatMediator {
    void sendMessage(String msg, User user);
    void addUser(User user);
}

然后实现具体的中介者——聊天室:

public class ChatRoom implements ChatMediator {
    private List<User> users = new ArrayList<>();
    
    @Override
    public void addUser(User user) {
        users.add(user);
    }
    
    @Override
    public void sendMessage(String msg, User sender) {
        for (User user : users) {
            // 消息不发给发送者自己
            if (user != sender) {
                user.receive(msg);
            }
        }
    }
}

再定义用户类(同事对象):

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 msg);
    public abstract void receive(String msg);
}

public class ChatUser extends User {
    public ChatUser(String name, ChatMediator mediator) {
        super(name, mediator);
    }
    
    @Override
    public void send(String msg) {
        System.out.println(name + " 发送: " + msg);
        mediator.sendMessage(msg, this);
    }
    
    @Override
    public void receive(String msg) {
        System.out.println(name + " 收到: " + msg);
    }
}

客户端调用:

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("大家好!");
// 输出:
// Alice 发送: 大家好!
// Bob 收到: 大家好!
// Charlie 收到: 大家好!

关键点:Alice 发送消息时,她根本不知道 Bob 和 Charlie 的存在。她只跟 chatRoom 打交道。如果后来加了新用户,Alice 的代码一行都不用改。

C++实现:同样的思路

#include <iostream>
#include <vector>
#include <string>
using namespace std;

// 前向声明
class User;

// 中介者接口
class ChatMediator {
public:
    virtual void sendMessage(const string& msg, User* sender) = 0;
    virtual void addUser(User* user) = 0;
    virtual ~ChatMediator() {}
};

// 同事基类
class User {
protected:
    string name;
    ChatMediator* mediator;
public:
    User(const string& n, ChatMediator* m) : name(n), mediator(m) {}
    virtual void send(const string& msg) = 0;
    virtual void receive(const string& msg) = 0;
    virtual ~User() {}
    string getName() const { return name; }
};

// 具体中介者
class ChatRoom : public ChatMediator {
private:
    vector<User*> users;
public:
    void addUser(User* user) override {
        users.push_back(user);
    }
    
    void sendMessage(const string& msg, User* sender) override {
        for (User* user : users) {
            if (user != sender) {
                user->receive(msg);
            }
        }
    }
};

// 具体用户
class ChatUser : public User {
public:
    ChatUser(const string& n, ChatMediator* m) : User(n, m) {}
    
    void send(const string& msg) override {
        cout << name << " 发送: " << msg << endl;
        mediator->sendMessage(msg, this);
    }
    
    void receive(const string& msg) override {
        cout << name << " 收到: " << msg << endl;
    }
};

// 使用示例
int main() {
    ChatRoom chatRoom;
    
    ChatUser alice("Alice", &chatRoom);
    ChatUser bob("Bob", &chatRoom);
    ChatUser charlie("Charlie", &chatRoom);
    
    chatRoom.addUser(&alice);
    chatRoom.addUser(&bob);
    chatRoom.addUser(&charlie);
    
    alice.send("大家好!");
    return 0;
}

中介者模式在MVC中的应用

说到这个,我不得不提MVC架构。你仔细想想,MVC里的Controller其实就是个中介者。

MVC组件 对应中介者模式 职责
View(视图) 同事对象 展示数据,接收用户输入
Model(模型) 同事对象 管理数据,业务逻辑
Controller(控制器) 中介者 协调View和Model的交互

View不直接操作Model,Model也不直接更新View。所有交互都通过Controller转发。这就是中介者模式在大型框架中的经典应用。

我的经验:在Spring MVC中,DispatcherServlet就是一个全局中介者。它接收所有HTTP请求,然后分发给对应的Controller处理。Controller再调用Service(Model层),最后返回ModelAndView给DispatcherServlet,由它决定渲染哪个View。

避坑指南

我曾经踩过的坑:中介者模式虽然解耦了同事对象,但中介者本身会变得非常复杂。如果你把所有逻辑都塞进中介者,它就成了一个“上帝对象”——什么都知道,什么都管。这会导致中介者难以维护。

怎么避免?我建议:

  • 职责单一:一个中介者只负责一类交互。比如聊天室中介者只管消息转发,不要让它管用户认证、文件传输。
  • 考虑事件驱动:如果交互逻辑太复杂,可以用事件总线(Event Bus)替代中介者。事件总线本质上也是一种中介者,但更灵活。
  • 不要滥用:如果只有两三个对象交互,直接通信反而更简单。中介者模式适合“多对多”的复杂场景。

什么时候用中介者模式?

我总结了几条判断标准:

  1. 对象之间的交互关系复杂,形成网状结构
  2. 多个对象需要互相通信,但你想让它们互相不知道对方
  3. 你想把交互逻辑集中管理,方便后续修改
  4. 系统需要支持动态增加或移除参与者

举个例子,我在做一个物联网平台时,设备、传感器、报警器、日志系统之间需要频繁通信。用中介者模式后,新增一种设备类型,只需要改中介者,其他设备完全不受影响。这就是解耦带来的好处。

嗯,中介者模式就聊到这儿。记住一句话:把复杂交互交给中介者,让每个对象只关心自己的事。这样你的代码会清爽很多。


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