1. 中介者模式的核心价值与应用场景
中介者模式(Mediator Pattern)是我在重构一个多人聊天室项目时深刻体会到其价值的设计模式。当系统中存在大量对象间的复杂网状交互时,这种模式能像交通警察一样有效协调各个对象之间的通信。
想象一下一个典型的航空管制系统:如果没有塔台作为中介者,每架飞机都需要直接与其他所有飞机通信以避免碰撞,这种网状连接将导致系统难以维护。中介者模式通过引入一个中间层,将原本对象间直接的多对多关系转化为一对多关系,显著降低了系统的耦合度。
在《Head First设计模式》的C++实现中,中介者模式特别适合以下场景:
- GUI组件间的交互(如按钮点击影响多个控件状态)
- 游戏开发中角色/道具的联动逻辑
- 分布式系统中的消息路由
- 工作流引擎中的步骤协调
2. C++实现中介者模式的经典结构
2.1 基础类图实现
用C++实现标准中介者模式时,通常包含以下核心组件:
cpp复制// 前置声明
class Colleague;
// 抽象中介者
class Mediator {
public:
virtual void notify(Colleague* sender, const std::string& event) = 0;
virtual ~Mediator() = default;
};
// 抽象同事类
class Colleague {
protected:
Mediator* mediator_;
public:
explicit Colleague(Mediator* mediator) : mediator_(mediator) {}
virtual void send(const std::string& event) = 0;
virtual void receive(const std::string& msg) = 0;
virtual ~Colleague() = default;
};
// 具体同事类示例
class ConcreteColleagueA : public Colleague {
public:
using Colleague::Colleague;
void send(const std::string& event) override {
mediator_->notify(this, event);
}
void receive(const std::string& msg) override {
std::cout << "ColleagueA received: " << msg << "\n";
}
};
2.2 具体中介者实现要点
在具体中介者实现时,需要注意引用管理的问题。以下是带智能指针的增强实现:
cpp复制class ConcreteMediator : public Mediator {
private:
std::vector<std::weak_ptr<Colleague>> colleagues_;
public:
void addColleague(const std::shared_ptr<Colleague>& col) {
colleagues_.push_back(col);
}
void notify(Colleague* sender, const std::string& event) override {
for (auto& wp_col : colleagues_) {
if (auto col = wp_col.lock()) {
if (col.get() != sender) {
col->receive("Forwarded: " + event);
}
}
}
}
};
关键技巧:使用weak_ptr避免循环引用问题,这是C++实现中介者模式时常见的坑
3. 实战:聊天室系统的中介者模式实现
3.1 场景分析与类设计
我们实现一个支持私聊和广播的聊天室系统:
mermaid复制classDiagram
class ChatRoomMediator {
-users: map<string, User*>
+registerUser(user: User*): void
+sendMessage(sender: User*, message: string, receiver?: string): void
}
class User {
-name: string
-mediator: ChatRoomMediator*
+send(message: string, to?: string): void
+receive(from: string, message: string): void
}
User "1" --> "1" ChatRoomMediator
3.2 完整C++实现代码
cpp复制#include <iostream>
#include <map>
#include <string>
#include <memory>
class User;
class ChatRoomMediator {
public:
virtual void registerUser(User* user) = 0;
virtual void sendMessage(User* sender, const std::string& message,
const std::string& receiver = "") = 0;
virtual ~ChatRoomMediator() = default;
};
class User {
protected:
std::string name_;
ChatRoomMediator* mediator_;
public:
User(const std::string& name, ChatRoomMediator* mediator)
: name_(name), mediator_(mediator) {}
void send(const std::string& message, const std::string& to = "") {
mediator_->sendMessage(this, message, to);
}
virtual void receive(const std::string& from, const std::string& message) {
std::cout << name_ << " received from " << from
<< ": " << message << "\n";
}
std::string getName() const { return name_; }
virtual ~User() = default;
};
class ConcreteChatRoom : public ChatRoomMediator {
private:
std::map<std::string, User*> users_;
public:
void registerUser(User* user) override {
users_[user->getName()] = user;
}
void sendMessage(User* sender, const std::string& message,
const std::string& receiver) override {
if (receiver.empty()) {
// 广播消息
for (auto& [name, user] : users_) {
if (user != sender) {
user->receive(sender->getName(), message);
}
}
} else {
// 私聊消息
if (auto it = users_.find(receiver); it != users_.end()) {
it->second->receive(sender->getName(), message);
}
}
}
};
3.3 使用示例与输出
cpp复制int main() {
auto chatRoom = std::make_unique<ConcreteChatRoom>();
auto alice = std::make_shared<User>("Alice", chatRoom.get());
auto bob = std::make_shared<User>("Bob", chatRoom.get());
auto charlie = std::make_shared<User>("Charlie", chatRoom.get());
chatRoom->registerUser(alice.get());
chatRoom->registerUser(bob.get());
chatRoom->registerUser(charlie.get());
alice->send("Hello everyone!"); // 广播
bob->send("Private message", "Alice"); // 私聊
return 0;
}
输出结果:
code复制Bob received from Alice: Hello everyone!
Charlie received from Alice: Hello everyone!
Alice received from Bob: Private message
4. 中介者模式的进阶应用技巧
4.1 与观察者模式的结合
中介者模式常与观察者模式结合使用,形成更灵活的事件处理机制:
cpp复制class EventMediator : public Mediator {
private:
std::unordered_map<std::string, std::vector<Colleague*>> eventSubscribers;
public:
void subscribe(const std::string& eventType, Colleague* col) {
eventSubscribers[eventType].push_back(col);
}
void notify(Colleague* sender, const std::string& event) override {
if (eventSubscribers.count(event)) {
for (auto col : eventSubscribers[event]) {
if (col != sender) {
col->receive(event);
}
}
}
}
};
4.2 线程安全实现
在多线程环境下使用中介者模式时,需要特别注意线程安全问题:
cpp复制#include <mutex>
class ThreadSafeMediator : public Mediator {
private:
std::vector<Colleague*> colleagues_;
mutable std::mutex mtx_;
public:
void addColleague(Colleague* col) {
std::lock_guard<std::mutex> lock(mtx_);
colleagues_.push_back(col);
}
void notify(Colleague* sender, const std::string& event) override {
std::vector<Colleague*> tempCopy;
{
std::lock_guard<std::mutex> lock(mtx_);
tempCopy = colleagues_;
}
for (auto col : tempCopy) {
if (col != sender) {
col->receive(event);
}
}
}
};
重要提示:在真实的项目中使用时,还需要考虑同事对象的生命周期管理问题
5. 模式对比与使用陷阱
5.1 中介者 vs 外观模式
虽然都是"中间层",但两者有本质区别:
| 特性 | 中介者模式 | 外观模式 |
|---|---|---|
| 交互方向 | 双向通信 | 单向简化接口 |
| 对象关系 | 同级对象间的协调 | 子系统对外的统一入口 |
| 复杂度 | 中介者本身可能变得复杂 | 外观通常保持简单 |
| 使用场景 | 对象间交互复杂且频繁 | 需要简化复杂子系统访问 |
5.2 常见实现陷阱
-
上帝对象反模式:中介者过度集中逻辑会变成难以维护的"上帝对象"
- 解决方案:按职责划分多个中介者
-
性能瓶颈:高频消息传递可能导致中介者成为性能瓶颈
- 解决方案:引入消息队列异步处理
-
循环通知:A通知B,B又通知A导致无限循环
- 解决方案:设置消息处理标志位
-
内存泄漏:C++中未正确管理同事对象生命周期
- 解决方案:使用智能指针或明确的注销机制
cpp复制// 改进的中介者注销机制
void ConcreteMediator::unregister(Colleague* col) {
colleagues_.erase(
std::remove_if(colleagues_.begin(), colleagues_.end(),
[col](const auto& wp) {
if (auto sp = wp.lock()) return sp.get() == col;
return false;
}),
colleagues_.end()
);
}
6. 现代C++中的优化实现
6.1 使用function/bind实现灵活回调
cpp复制#include <functional>
#include <unordered_map>
class ModernMediator {
private:
using Handler = std::function<void(const std::string&)>;
std::unordered_map<std::string, std::vector<Handler>> handlers;
public:
void registerHandler(const std::string& event, Handler handler) {
handlers[event].push_back(handler);
}
void broadcast(const std::string& event, const std::string& msg) {
if (handlers.count(event)) {
for (auto& handler : handlers[event]) {
handler(msg);
}
}
}
};
6.2 基于模板的通用实现
cpp复制template<typename T>
class GenericMediator {
private:
std::vector<std::weak_ptr<T>> colleagues;
public:
void addColleague(std::shared_ptr<T> col) {
colleagues.push_back(col);
}
template<typename Event>
void notify(const Event& event) {
for (auto& wp : colleagues) {
if (auto sp = wp.lock()) {
sp->handleEvent(event);
}
}
}
};
使用示例:
cpp复制struct ChatEvent {
std::string from;
std::string message;
};
class ChatParticipant {
public:
virtual void handleEvent(const ChatEvent& event) = 0;
virtual ~ChatParticipant() = default;
};
7. 测试策略与Mock实现
7.1 单元测试要点
对中介者模式进行测试时,需要关注:
- 消息路由的正确性
- 异常情况处理(如未注册的同事)
- 性能基准测试(高并发场景)
cpp复制#include <gtest/gtest.h>
TEST(MediatorTest, MessageRouting) {
auto mediator = std::make_shared<ConcreteMediator>();
auto col1 = std::make_shared<MockColleague>(mediator.get());
auto col2 = std::make_shared<MockColleague>(mediator.get());
mediator->addColleague(col1);
mediator->addColleague(col2);
EXPECT_CALL(*col2, receive("test")).Times(1);
col1->send("test");
}
7.2 模拟对象实现
cpp复制class MockColleague : public Colleague {
public:
MOCK_METHOD(void, receive, (const std::string&), (override));
MockColleague(Mediator* med) : Colleague(med) {}
void send(const std::string& event) override {
mediator_->notify(this, event);
}
};
8. 性能优化与扩展方向
8.1 消息过滤机制
通过添加谓词实现高效的消息过滤:
cpp复制void notify(Colleague* sender, const std::string& event,
std::function<bool(Colleague*)> predicate = nullptr)
{
for (auto& wp : colleagues_) {
if (auto sp = wp.lock()) {
if (sp.get() != sender && (!predicate || predicate(sp.get()))) {
sp->receive(event);
}
}
}
}
8.2 分布式中介者
通过引入网络层,实现跨进程的中介者模式:
cpp复制class NetworkMediator : public Mediator {
private:
NetworkClient& network_;
public:
void notify(Colleague* sender, const std::string& event) override {
network_.broadcast(event);
}
void onMessageReceived(const std::string& msg) {
for (auto& wp : colleagues_) {
if (auto sp = wp.lock()) {
sp->receive(msg);
}
}
}
};
在实际项目中,中介者模式的威力往往在系统演化过程中逐渐显现。当发现对象间的依赖关系变得错综复杂时,引入中介者就像在混乱的十字路口安装交通信号灯,能立即带来秩序和清晰度。但也要警惕不要过度使用——就像现实世界中过多的红绿灯反而会降低交通效率一样,设计时需要权衡集中控制和灵活性的关系。