中介者模式(Mediator Pattern) 是一种行为设计模式,它的核心思想非常直观:用一个"中间人"对象来封装一系列对象之间的交互
中介者模式的作用:
想象一个没有中介者的场景(比如一个复杂的对话框 UI)
有一个"登录按钮"、一个"用户名输入框"、一个"密码输入框"、一个"重置按钮"。
逻辑耦合:用户名输入框需要知道登录按钮的存在(如果用户名为空,禁用按钮)。密码输入框也需要知道登录按钮的存在。重置按钮需要知道用户名和密码框(点击重置要清空它们)。如果再加一个"验证码输入框",它又要去修改前面所有的组件状态。
结果 :对象之间形成了网状依赖(Many-to-Many)。每个对象都引用了其他多个对象,代码难以维护,改一个地方可能崩一片。
中介者模式的解决方案 :
引入一个**中介者(Mediator)**对象。
所有组件(同事类)只与中介者通信,不再直接互相引用。
组件 A 发生变化 -> 通知中介者 -> 中介者决定通知组件 B 和 C。
结果 :网状依赖变成了星状依赖(Many-to-One)
为了便于理解,外面举一个聊天室系统的例子:
同事类 (Colleague) :User(用户),他们只知道中介者,不知道其他用户。
抽象中介者 (Mediator) :ChatRoomMediator,定义发送消息的接口。
具体中介者 (ConcreteMediator) :ChatRoom,维护用户列表,处理消息转发逻辑
cpp
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <algorithm>
// 前向声明
class ChatRoomMediator;
class User;
/**
* 2. 抽象中介者 (Mediator)
* 定义同事对象之间通信的接口
*/
class ChatRoomMediator {
public:
virtual ~ChatRoomMediator() = default;
// 发送消息:message是内容,sender是发送者
virtual void sendMessage(const std::string& message, const User* sender) = 0;
// 添加用户到中介者
virtual void addUser(std::shared_ptr<User> user) = 0;
};
/**
* 3. 抽象同事类 (Colleague)
* 持有对中介者的引用
*/
class User {
protected:
std::string name;
std::weak_ptr<ChatRoomMediator> mediator; // 使用 weak_ptr 防止循环引用
public:
explicit User(const std::string& n) : name(n) {}
virtual ~User() = default;
// 设置中介者 (由具体中介者在添加用户时调用)
void setMediator(std::shared_ptr<ChatRoomMediator> med) {
mediator = med;
}
const std::string& getName() const { return name; }
// 发送消息接口
virtual void send(const std::string& message) = 0;
// 接收消息接口
virtual void receive(const std::string& message, const std::string& senderName) = 0;
};
/**
* 4. 具体中介者 (ConcreteMediator)
* 协调各个同事对象之间的交互
*/
class ChatRoom : public ChatRoomMediator {
private:
std::vector<std::shared_ptr<User>> users;
public:
void addUser(std::shared_ptr<User> user) override {
users.push_back(user);
// 关键:将中介者自己注入给用户,建立连接
user->setMediator(shared_from_this());
}
void sendMessage(const std::string& message, const User* sender) override {
std::cout << "[聊天室] " << sender->getName() << " 发送消息: \"" << message << "\"\n";
// 遍历所有用户,将消息转发给除了发送者以外的所有人
for (const auto& user : users) {
// 注意:这里通过指针比较来判断是否是发送者
if (user.get() != sender) {
user->receive(message, sender->getName());
}
}
}
};
/**
* 4. 具体同事类 (ConcreteColleague)
* 具体的用户实现
*/
class ChatUser : public User {
public:
explicit ChatUser(const std::string& n) : User(n) {}
void send(const std::string& message) override {
// 锁定 weak_ptr 获取 shared_ptr,确保中介者还活着
if (auto med = mediator.lock()) {
med->sendMessage(message, this);
} else {
std::cerr << "[错误] " << name << " 无法发送消息,未连接到聊天室。\n";
}
}
void receive(const std::string& message, const std::string& senderName) override {
std::cout << " -> [" << name << "] 收到来自 " << senderName << " 的消息: " << message << "\n";
}
};
// --- 客户端代码 ---
int main() {
// 1. 创建具体中介者 (聊天室)
// 必须使用 shared_ptr,因为 User 需要持有它,且 ChatRoom 内部可能需要 shared_from_this
auto chatRoom = std::make_shared<ChatRoom>();
// 2. 创建具体同事 (用户)
auto alice = std::make_shared<ChatUser>("Alice");
auto bob = std::make_shared<ChatUser>("Bob");
auto charlie = std::make_shared<ChatUser>("Charlie");
// 3. 将用户注册到中介者
// 这一步会自动调用 user->setMediator(chatRoom)
chatRoom->addUser(alice);
chatRoom->addUser(bob);
chatRoom->addUser(charlie);
std::cout << "=== 聊天开始 ===" << std::endl;
// 4. 用户发送消息
// Alice 发送消息,她不需要知道 Bob 和 Charlie 的存在
alice->send("大家好,我是新来的!");
std::cout << "----------------" << std::endl;
// Bob 回复
bob->send("欢迎 Alice!我是 Bob。");
std::cout << "----------------" << std::endl;
// Charlie 私聊逻辑模拟(在中介者中扩展即可,这里演示群发)
charlie->send("嗨,大家晚上好。");
return 0;
}
代码关键点解析
智能指针管理 (std::shared_ptr & std::weak_ptr):
ChatRoom 和 User 互相持有对方。如果都用 shared_ptr,会造成循环引用 ,导致内存泄漏。解决方案:ChatRoom 持有 User 的 shared_ptr(拥有权),而 User 持有 ChatRoom 的 weak_ptr(观察权)。在 User::send 方法中,使用 mediator.lock() 临时提升为 shared_ptr 以确保安全调用。
解耦体现:
ChatUser 类中没有任何关于其他 ChatUser 的代码(没有 #include 其他用户类,也没有成员变量指向其他用户)。如果要增加"禁言"功能,只需修改 ChatRoom::sendMessage,无需改动 ChatUser
预期输出:
cpp
=== 聊天开始 ===
[聊天室] Alice 发送消息: "大家好,我是新来的!"
-> [Bob] 收到来自 Alice 的消息: 大家好,我是新来的!
-> [Charlie] 收到来自 Alice 的消息: 大家好,我是新来的!
----------------
[聊天室] Bob 发送消息: "欢迎 Alice!我是 Bob。"
-> [Alice] 收到来自 Bob 的消息: 欢迎 Alice!我是 Bob。
-> [Charlie] 收到来自 Bob 的消息: 欢迎 Alice!我是 Bob。
----------------
[聊天室] Charlie 发送消息: "嗨,大家晚上好。"
-> [Alice] 收到来自 Charlie 的消息: 嗨,大家晚上好。
-> [Bob] 收到来自 Charlie 的消息: 嗨,大家晚上好。