中介者模式:对象间通信解耦,C++中的聊天室系统

聊到中介者模式,我脑子里第一个蹦出来的场景就是——聊天室。你想想看,如果没有聊天室这个"中间人",每个人想跟别人说话就得直接连过去,那得多乱?每个人都要维护一堆连接,还要知道谁在线谁离线。嗯,这就是中介者模式要解决的问题。

什么是中介者模式?

说白了,中介者模式就是用一个中介对象来封装一组对象之间的交互。这些对象不再直接互相引用,而是通过中介者来通信。这样就把多对多的关系,简化成了一对多的关系。

我在项目中遇到过这样一个场景:一个复杂的UI界面,有十几个控件,它们之间互相影响。比如下拉框变了,表格要刷新,按钮状态要变,状态栏要更新。如果让这些控件直接通信,那代码就成了一团乱麻。用中介者模式,所有控件只跟中介者说话,清爽多了。

核心结构

中介者模式有四个角色:

  • Mediator(抽象中介者):定义通信接口
  • ConcreteMediator(具体中介者):协调各个同事对象
  • Colleague(抽象同事类):每个同事都持有中介者的引用
  • ConcreteColleague(具体同事类):实际的通信参与者

核心思想:同事对象之间不直接通信,而是通过中介者转发消息。这样每个同事只需要知道中介者,不需要知道其他同事的存在。

聊天室系统的C++实现

我们来实现一个简单的聊天室系统。这个例子很经典,也最能说明中介者模式的价值。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

// 前向声明
class User;

// 抽象中介者 - 聊天室
class ChatRoomMediator {
public:
    virtual ~ChatRoomMediator() = default;
    virtual void sendMessage(User* sender, const std::string& message) = 0;
    virtual void addUser(User* user) = 0;
};

// 抽象同事类 - 用户
class User {
protected:
    std::string name;
    ChatRoomMediator* mediator;
    
public:
    User(const std::string& name, ChatRoomMediator* med)
        : name(name), mediator(med) {}
    
    virtual ~User() = default;
    
    std::string getName() const { return name; }
    
    virtual void send(const std::string& message) {
        mediator->sendMessage(this, message);
    }
    
    virtual void receive(const std::string& senderName, 
                         const std::string& message) {
        std::cout << "[" << name << "] 收到来自 " 
                  << senderName << " 的消息: " 
                  << message << std::endl;
    }
};

// 具体中介者 - 聊天室实现
class ChatRoom : public ChatRoomMediator {
private:
    std::vector<User*> users;
    
public:
    void addUser(User* user) override {
        users.push_back(user);
        std::cout << "[" << user->getName() 
                  << "] 加入了聊天室" << std::endl;
    }
    
    void sendMessage(User* sender, const std::string& message) override {
        for (User* user : users) {
            // 不给自己发消息
            if (user != sender) {
                user->receive(sender->getName(), message);
            }
        }
    }
    
    void removeUser(User* user) {
        auto it = std::find(users.begin(), users.end(), user);
        if (it != users.end()) {
            users.erase(it);
            std::cout << "[" << user->getName() 
                      << "] 离开了聊天室" << std::endl;
        }
    }
};

// 具体同事类 - 普通用户
class RegularUser : public User {
public:
    RegularUser(const std::string& name, ChatRoomMediator* med)
        : User(name, med) {}
    
    void send(const std::string& message) override {
        std::cout << "[" << name << "] 发送消息: " 
                  << message << std::endl;
        User::send(message);
    }
};

// 具体同事类 - 管理员
class AdminUser : public User {
public:
    AdminUser(const std::string& name, ChatRoomMediator* med)
        : User(name, med) {}
    
    void send(const std::string& message) override {
        std::cout << "[管理员 " << name << "] 发送系统消息: " 
                  << message << std::endl;
        User::send(message);
    }
    
    void broadcast(const std::string& message) {
        std::string sysMsg = "[系统公告] " + message;
        send(sysMsg);
    }
};

// 使用示例
int main() {
    ChatRoom chatRoom;
    
    RegularUser alice("Alice", &chatRoom);
    RegularUser bob("Bob", &chatRoom);
    AdminUser admin("Admin", &chatRoom);
    
    chatRoom.addUser(&alice);
    chatRoom.addUser(&bob);
    chatRoom.addUser(&admin);
    
    std::cout << "\n--- 普通聊天 ---" << std::endl;
    alice.send("大家好!");
    bob.send("嗨,Alice!");
    
    std::cout << "\n--- 管理员广播 ---" << std::endl;
    admin.broadcast("今晚系统维护,请提前下线。");
    
    return 0;
}

运行结果

[Alice] 加入了聊天室
[Bob] 加入了聊天室
[Admin] 加入了聊天室

--- 普通聊天 ---
[Alice] 发送消息: 大家好!
[Bob] 收到来自 Alice 的消息: 大家好!
[Admin] 收到来自 Alice 的消息: 大家好!
[Bob] 发送消息: 嗨,Alice!
[Alice] 收到来自 Bob 的消息: 嗨,Alice!
[Admin] 收到来自 Bob 的消息: 嗨,Alice!

--- 管理员广播 ---
[管理员 Admin] 发送系统消息: [系统公告] 今晚系统维护,请提前下线。
[Alice] 收到来自 Admin 的消息: [系统公告] 今晚系统维护,请提前下线。
[Bob] 收到来自 Admin 的消息: [系统公告] 今晚系统维护,请提前下线。

中介者模式的核心逻辑

我画了一张图,帮你理清这个模式的通信流程:

中介者模式通信流程 ChatRoom(中介者) User A(同事) User B(同事) User C(同事) 发送消息 转发 转发 关键点:同事之间不直接通信,全部通过中介者转发 多对多 → 一对多 每个同事只依赖中介者,不依赖其他同事

什么时候用中介者模式?

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

  • 对象间通信太复杂:当多个对象之间存在网状引用关系时
  • 想复用对象:让对象不依赖于其他具体对象,更容易复用
  • 通信逻辑需要集中管理:比如聊天室的过滤、日志、权限控制

优缺点分析

优点 缺点
降低了对象间的耦合度 中介者可能变得庞大复杂
将多对多通信简化为一对多 中介者出问题会影响所有同事
通信逻辑集中,便于维护 同事类只能通过中介者通信,效率略低
新增同事类不需要修改现有代码 设计不当会导致中介者成为上帝对象

避坑指南:我曾经在一个项目中把中介者写得过于"万能",结果它变成了一个上帝对象,什么逻辑都往里塞。最后中介者自己就成了一团乱麻。记住,中介者只负责协调通信,不要把业务逻辑也塞进去。

与观察者模式的区别

很多人会把中介者模式和观察者模式搞混。我简单说一下区别:

  • 观察者模式:一对多,一个主题通知多个观察者。通信是单向的。
  • 中介者模式:多对多,中介者协调多个同事之间的双向通信。

说白了,观察者模式适合"发布-订阅"场景,中介者模式适合"多方对话"场景。

小技巧:如果发现你的中介者开始处理业务逻辑、做数据转换、管理状态,那就要警惕了。中介者只应该做一件事:转发消息。其他的事情交给同事类自己处理。

实际项目中的应用

除了聊天室,中介者模式在现实项目中还有很多应用:

  • GUI框架:对话框作为中介者,协调按钮、文本框、下拉框等控件
  • 消息中间件:消息队列作为中介者,解耦生产者和消费者
  • 机场调度系统:塔台作为中介者,协调飞机的起降
  • 微服务网关:API网关作为中介者,协调各个微服务之间的调用

嗯,中介者模式就是这么个东西。它不复杂,但用好了能让你的代码结构清晰很多。记住核心:让对象只跟中介者说话,别直接找别人


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