中介者模式:对象间通信解耦,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 的消息: [系统公告] 今晚系统维护,请提前下线。
中介者模式的核心逻辑
我画了一张图,帮你理清这个模式的通信流程:
什么时候用中介者模式?
我个人习惯在以下几种场景中使用:
- 对象间通信太复杂:当多个对象之间存在网状引用关系时
- 想复用对象:让对象不依赖于其他具体对象,更容易复用
- 通信逻辑需要集中管理:比如聊天室的过滤、日志、权限控制
优缺点分析
| 优点 | 缺点 |
|---|---|
| 降低了对象间的耦合度 | 中介者可能变得庞大复杂 |
| 将多对多通信简化为一对多 | 中介者出问题会影响所有同事 |
| 通信逻辑集中,便于维护 | 同事类只能通过中介者通信,效率略低 |
| 新增同事类不需要修改现有代码 | 设计不当会导致中介者成为上帝对象 |
避坑指南:我曾经在一个项目中把中介者写得过于"万能",结果它变成了一个上帝对象,什么逻辑都往里塞。最后中介者自己就成了一团乱麻。记住,中介者只负责协调通信,不要把业务逻辑也塞进去。
与观察者模式的区别
很多人会把中介者模式和观察者模式搞混。我简单说一下区别:
- 观察者模式:一对多,一个主题通知多个观察者。通信是单向的。
- 中介者模式:多对多,中介者协调多个同事之间的双向通信。
说白了,观察者模式适合"发布-订阅"场景,中介者模式适合"多方对话"场景。
小技巧:如果发现你的中介者开始处理业务逻辑、做数据转换、管理状态,那就要警惕了。中介者只应该做一件事:转发消息。其他的事情交给同事类自己处理。
实际项目中的应用
除了聊天室,中介者模式在现实项目中还有很多应用:
- GUI框架:对话框作为中介者,协调按钮、文本框、下拉框等控件
- 消息中间件:消息队列作为中介者,解耦生产者和消费者
- 机场调度系统:塔台作为中介者,协调飞机的起降
- 微服务网关:API网关作为中介者,协调各个微服务之间的调用
嗯,中介者模式就是这么个东西。它不复杂,但用好了能让你的代码结构清晰很多。记住核心:让对象只跟中介者说话,别直接找别人。
公众号:蓝海资料掘金营,微信deep3321