设计模式(C++)详解——中介者模式(3)
<摘要>
中介者模式是一种行为设计模式,通过引入中介者对象来封装一组对象之间的交互,从而降低对象间的耦合度。本文将详细解析中介者模式的背景概念、设计意图、实现方式及应用场景,通过聊天室系统、飞机调度系统和UI组件交互三个完整案例,结合C++代码实现和Mermaid图表,全面展示该模式的实际应用价值。
<解析>
中介者模式深度解析
1. 背景与核心概念
1.1 起源与发展历程
中介者模式的诞生可以追溯到软件工程中"高内聚、低耦合"的设计原则。在早期的面向对象设计中,开发人员经常遇到这样的困境:当多个对象需要相互通信时,直接的对象引用会导致复杂的网状依赖关系。
发展脉络:
- 1987年:在Smalltalk语言的MVC框架中首次出现了类似中介者的概念
- 1994年:Gamma等人在《设计模式》一书中正式提出并定义了中介者模式
- 2000年代:随着GUI框架和企业级应用的发展,中介者模式得到广泛应用
- 现今:在微服务架构、事件驱动系统中,中介者模式的思想被重新诠释和应用
1.2 核心概念解析
中介者模式的核心思想是:用一个中介对象来封装一系列的对象交互,使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
关键术语:
术语 | 英文 | 说明 |
---|---|---|
中介者 | Mediator | 定义对象间交互的接口 |
具体中介者 | ConcreteMediator | 实现中介者接口,协调各同事对象 |
同事类 | Colleague | 定义同事类的接口,持有中介者引用 |
具体同事类 | ConcreteColleague | 实现同事类接口,只与中介者通信 |
1.3 UML类图解析
图表解读:
- Mediator:抽象中介者,定义通信接口
- ConcreteMediator:具体中介者,维护同事对象引用,实现复杂的协调逻辑
- Colleague:同事抽象类,包含中介者引用
- ConcreteColleague:具体同事类,通过中介者与其他对象通信
2. 设计意图与考量
2.1 核心设计目标
中介者模式的核心设计目标可以用"解耦"二字概括,具体体现在:
- 降低耦合度:将网状依赖变为星状依赖
- 集中控制:将交互逻辑集中到中介者中
- 简化对象职责:每个对象只需关注自身业务逻辑
- 提高复用性:同事类可以独立复用
2.2 设计权衡因素
优势:
- ✅ 减少类间依赖,降低耦合
- ✅ 简化对象协议,一对多交互变为一对一
- ✅ 抽象对象交互,便于理解和维护
- ✅ 符合迪米特法则(最少知识原则)
劣势:
- ❌ 中介者可能变得过于复杂,成为"上帝对象"
- ❌ 增加了系统的中间层,可能影响性能
- ❌ 中介者本身的修改可能影响整个系统
2.3 适用场景分析
适合使用中介者模式的场景:
- 对象间存在复杂的网状引用关系
- 一个对象引用很多其他对象,直接通信导致难以复用
- 想在多个对象间定制交互行为,又不想生成太多子类
- 交互行为需要定义在不同对象间,但又不想分散在各个类中
3. 实例与应用场景
3.1 案例一:智能聊天室系统
场景描述:
设计一个多人聊天室系统,用户可以向所有人广播消息,也可以私聊特定用户,同时需要支持用户加入、离开的通知。
实现方案:
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
#include <memory>// 前向声明
class User;/*** @brief 聊天室中介者接口* * 定义用户间通信的标准接口,负责协调所有用户对象之间的交互*/
class ChatMediator {
public:virtual ~ChatMediator() = default;/*** @brief 发送消息到所有用户* * @param message 消息内容* @param sender 发送者*/virtual void broadcastMessage(const std::string& message, const std::string& sender) = 0;/*** @brief 发送私聊消息* * @param message 消息内容* @param sender 发送者* @param receiver 接收者*/virtual void privateMessage(const std::string& message,const std::string& sender,const std::string& receiver) = 0;/*** @brief 用户加入聊天室* * @param user 用户对象*/virtual void addUser(std::shared_ptr<User> user) = 0;/*** @brief 用户离开聊天室* * @param username 用户名*/virtual void removeUser(const std::string& username) = 0;
};/*** @brief 用户抽象类(同事类)* * 定义用户的基本行为和属性,持有中介者引用*/
class User {
protected:std::string username_;std::shared_ptr<ChatMediator> mediator_;public:User(const std::string& username, std::shared_ptr<ChatMediator> mediator): username_(username), mediator_(mediator) {}virtual ~User() = default;/*** @brief 获取用户名* * @return std::string 用户名*/std::string getUsername() const { return username_; }/*** @brief 发送广播消息* * @param message 消息内容*/virtual void sendMessage(const std::string& message) {std::cout << username_ << " 发送广播: " << message << std::endl;mediator_->broadcastMessage(message, username_);}/*** @brief 发送私聊消息* * @param message 消息内容* @param receiver 接收者用户名*/virtual void sendPrivateMessage(const std::string& message, const std::string& receiver) {std::cout << username_ << " 私聊 " << receiver << ": " << message << std::endl;mediator_->privateMessage(message, username_, receiver);}/*** @brief 接收消息* * @param message 消息内容* @param sender 发送者*/virtual void receiveMessage(const std::string& message, const std::string& sender) = 0;
};/*** @brief 具体用户类* * 实现具体的用户行为,可以扩展不同的用户类型*/
class ChatUser : public User {
public:ChatUser(const std::string& username, std::shared_ptr<ChatMediator> mediator): User(username, mediator) {}void receiveMessage(const std::string& message, const std::string& sender) override {std::cout << username_ << " 收到来自 " << sender << " 的消息: " << message << std::endl;}
};/*** @brief 具体聊天室中介者* * 实现聊天室的具体逻辑,管理所有用户和消息路由*/
class ConcreteChatMediator : public ChatMediator {
private:std::unordered_map<std::string, std::shared_ptr<User>> users_;public:void broadcastMessage(const std::string& message, const std::string& sender) override {std::cout << "=== 聊天室广播 === (" << sender << "): " << message << std::endl;for (const auto& pair : users_) {if (pair.first != sender) { // 不发送给自己pair.second->receiveMessage(message, sender);}}}void privateMessage(const std::string& message,const std::string& sender,const std::string& receiver) override {std::cout << "=== 私聊消息 === [" << sender << " -> " << receiver << "]: " << message << std::endl;auto it = users_.find(receiver);if (it != users_.end()) {it->second->receiveMessage("[私聊] " + message, sender);} else {// 通知发送者接收者不存在auto senderIt = users_.find(sender);if (senderIt != users_.end()) {senderIt->second->receiveMessage("错误: 用户 " + receiver + " 不存在", "系统");}}}void addUser(std::shared_ptr<User> user) override {std::string username = user->getUsername();users_[username] = user;// 通知所有用户有新用户加入broadcastMessage("用户 " + username + " 加入了聊天室", "系统");}void removeUser(const std::string& username) override {auto it = users_.find(username);if (it != users_.end()) {users_.erase(it);broadcastMessage("用户 " + username + " 离开了聊天室", "系统");}}
};// 演示代码
int main() {// 创建聊天室中介者auto chatMediator = std::make_shared<ConcreteChatMediator>();// 创建用户auto user1 = std::make_shared<ChatUser>("Alice", chatMediator);auto user2 = std::make_shared<ChatUser>("Bob", chatMediator);auto user3 = std::make_shared<ChatUser>("Charlie", chatMediator);// 用户加入聊天室chatMediator->addUser(user1);chatMediator->addUser(user2);chatMediator->addUser(user3);std::cout << "\n=== 聊天开始 ===\n" << std::endl;// 用户间通信user1->sendMessage("大家好!");user2->sendMessage("你好,Alice!");user1->sendPrivateMessage("嘿,Bob,有个秘密要告诉你", "Bob");user3->sendMessage("我也可以看到广播消息");// Bob离开聊天室chatMediator->removeUser("Bob");// 再次尝试给Bob发消息user1->sendPrivateMessage("Bob,你还在吗?", "Bob");return 0;
}
时序图分析:
3.2 案例二:飞机调度系统
场景描述:
设计一个航空管制系统,多架飞机需要与塔台通信,飞机之间不能直接通信,必须通过塔台中介者来协调起飞、降落和航线冲突。
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <algorithm>// 飞机状态枚举
enum class FlightStatus {ON_GROUND,TAKING_OFF,IN_AIR,LANDING
};/*** @brief 航空管制中介者接口* * 协调所有飞机的起降和飞行状态,避免冲突*/
class AirTrafficControl {
public:virtual ~AirTrafficControl() = default;/*** @brief 请求起飞* * @param flightId 航班号* @return bool 是否允许起飞*/virtual bool requestTakeoff(const std::string& flightId) = 0;/*** @brief 请求降落* * @param flightId 航班号* @return bool 是否允许降落*/virtual bool requestLanding(const std::string& flightId) = 0;/*** @brief 通知起飞完成* * @param flightId 航班号*/virtual void notifyTakeoffComplete(const std::string& flightId) = 0;/*** @brief 通知降落完成* * @param flightId 航班号*/virtual void notifyLandingComplete(const std::string& flightId) = 0;/*** @brief 注册飞机* * @param aircraft 飞机对象*/virtual void registerAircraft(std::shared_ptr<class Aircraft> aircraft) = 0;
};/*** @brief 飞机抽象类* * 定义飞机的基本行为和状态*/
class Aircraft {
protected:std::string flightId_;FlightStatus status_;std::shared_ptr<AirTrafficControl> atc_;public:Aircraft(const std::string& flightId, std::shared_ptr<AirTrafficControl> atc): flightId_(flightId), status_(FlightStatus::ON_GROUND), atc_(atc) {}virtual ~Aircraft() = default;std::string getFlightId() const { return flightId_; }FlightStatus getStatus() const { return status_; }/*** @brief 请求起飞*/virtual void requestTakeoff() {std::cout << "航班 " << flightId_ << " 请求起飞" << std::endl;if (atc_->requestTakeoff(flightId_)) {status_ = FlightStatus::TAKING_OFF;std::cout << "航班 " << flightId_ << " 开始起飞" << std::endl;} else {std::cout << "航班 " << flightId_ << " 起飞请求被拒绝" << std::endl;}}/*** @brief 请求降落*/virtual void requestLanding() {std::cout << "航班 " << flightId_ << " 请求降落" << std::endl;if (atc_->requestLanding(flightId_)) {status_ = FlightStatus::LANDING;std::cout << "航班 " << flightId_ << " 开始降落" << std::endl;} else {std::cout << "航班 " << flightId_ << " 降落请求被拒绝" << std::endl;}}/*** @brief 通知起飞完成*/virtual void notifyTakeoffComplete() {status_ = FlightStatus::IN_AIR;atc_->notifyTakeoffComplete(flightId_);std::cout << "航班 " << flightId_ << " 起飞完成,正在飞行" << std::endl;}/*** @brief 通知降落完成*/virtual void notifyLandingComplete() {status_ = FlightStatus::ON_GROUND;atc_->notifyLandingComplete(flightId_);std::cout << "航班 " << flightId_ << " 降落完成" << std::endl;}
};/*** @brief 具体飞机类*/
class CommercialAircraft : public Aircraft {
public:CommercialAircraft(const std::string& flightId, std::shared_ptr<AirTrafficControl> atc): Aircraft(flightId, atc) {}
};/*** @brief 具体航空管制中介者*/
class ConcreteATC : public AirTrafficControl {
private:std::vector<std::shared_ptr<Aircraft>> aircrafts_;int runwayInUse_; // 跑道使用状态public:ConcreteATC() : runwayInUse_(-1) {}bool requestTakeoff(const std::string& flightId) override {// 检查跑道是否被占用if (runwayInUse_ != -1) {std::cout << "塔台回复 " << flightId << ": 跑道忙碌,请等待" << std::endl;return false;}// 分配跑道for (size_t i = 0; i < aircrafts_.size(); ++i) {if (aircrafts_[i]->getFlightId() == flightId) {runwayInUse_ = i;std::cout << "塔台回复 " << flightId << ": 允许起飞,使用跑道" << std::endl;return true;}}return false;}bool requestLanding(const std::string& flightId) override {// 检查跑道是否被占用if (runwayInUse_ != -1) {std::cout << "塔台回复 " << flightId << ": 跑道忙碌,请盘旋等待" << std::endl;return false;}// 分配跑道for (size_t i = 0; i < aircrafts_.size(); ++i) {if (aircrafts_[i]->getFlightId() == flightId) {runwayInUse_ = i;std::cout << "塔台回复 " << flightId << ": 允许降落,使用跑道" << std::endl;return true;}}return false;}void notifyTakeoffComplete(const std::string& flightId) override {std::cout << "塔台确认: " << flightId << " 起飞完成,释放跑道" << std::endl;runwayInUse_ = -1;}void notifyLandingComplete(const std::string& flightId) override {std::cout << "塔台确认: " << flightId << " 降落完成,释放跑道" << std::endl;runwayInUse_ = -1;}void registerAircraft(std::shared_ptr<Aircraft> aircraft) override {aircrafts_.push_back(aircraft);std::cout << "塔台: 注册航班 " << aircraft->getFlightId() << std::endl;}
};// 演示代码
int main() {// 创建航空管制中心auto atc = std::make_shared<ConcreteATC>();// 创建飞机auto flight1 = std::make_shared<CommercialAircraft>("CA123", atc);auto flight2 = std::make_shared<CommercialAircraft>("UA456", atc);auto flight3 = std::make_shared<CommercialAircraft>("DL789", atc);// 注册飞机atc->registerAircraft(flight1);atc->registerAircraft(flight2);atc->registerAircraft(flight3);std::cout << "\n=== 航空管制演示 ===\n" << std::endl;// 飞机操作flight1->requestTakeoff(); // 应该成功flight1->notifyTakeoffComplete();flight2->requestTakeoff(); // 应该成功flight3->requestLanding(); // 应该被拒绝(跑道忙碌)flight2->notifyTakeoffComplete();flight3->requestLanding(); // 现在应该成功return 0;
}
3.3 案例三:UI组件交互系统
场景描述:
设计一个用户界面,包含多个交互组件(按钮、文本框、复选框等),这些组件之间的行为相互影响,但组件之间不直接通信,通过中介者协调。
#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <functional>// 前向声明
class UIComponent;/*** @brief UI中介者接口* * 协调UI组件之间的交互行为*/
class UIMediator {
public:virtual ~UIMediator() = default;/*** @brief 组件状态变化通知* * @param component 发生变化的组件* @param event 事件类型*/virtual void notify(std::shared_ptr<UIComponent> component, const std::string& event) = 0;/*** @brief 注册组件* * @param id 组件ID* @param component 组件对象*/virtual void registerComponent(const std::string& id, std::shared_ptr<UIComponent> component) = 0;
};/*** @brief UI组件抽象类*/
class UIComponent {
protected:std::string id_;bool enabled_;std::shared_ptr<UIMediator> mediator_;public:UIComponent(const std::string& id, std::shared_ptr<UIMediator> mediator): id_(id), enabled_(true), mediator_(mediator) {}virtual ~UIComponent() = default;std::string getId() const { return id_; }bool isEnabled() const { return enabled_; }virtual void setEnabled(bool enabled) {enabled_ = enabled;std::cout << "组件 " << id_ << " " << (enabled ? "启用" : "禁用") << std::endl;}/*** @brief 触发组件行为*/virtual void trigger() {if (enabled_) {std::cout << "组件 " << id_ << " 被触发" << std::endl;mediator_->notify(shared_from_this(), "triggered");}}/*** @brief 处理来自中介者的通知* * @param event 事件类型* @param data 事件数据*/virtual void handleEvent(const std::string& event, const std::string& data = "") = 0;
};/*** @brief 按钮组件*/
class Button : public UIComponent, public std::enable_shared_from_this<Button> {
public:Button(const std::string& id, std::shared_ptr<UIMediator> mediator): UIComponent(id, mediator) {}void handleEvent(const std::string& event, const std::string& data = "") override {if (event == "disable_buttons") {setEnabled(false);} else if (event == "enable_buttons") {setEnabled(true);}}
};/*** @brief 文本框组件*/
class TextBox : public UIComponent, public std::enable_shared_from_this<TextBox> {
private:std::string text_;public:TextBox(const std::string& id, std::shared_ptr<UIMediator> mediator): UIComponent(id, mediator), text_("") {}void setText(const std::string& text) {text_ = text;std::cout << "文本框 " << id_ << " 内容设置为: " << text << std::endl;mediator_->notify(shared_from_this(), "text_changed");}std::string getText() const { return text_; }void handleEvent(const std::string& event, const std::string& data = "") override {if (event == "clear_text") {setText("");} else if (event == "disable_input") {setEnabled(false);}}
};/*** @brief 复选框组件*/
class CheckBox : public UIComponent, public std::enable_shared_from_this<CheckBox> {
private:bool checked_;public:CheckBox(const std::string& id, std::shared_ptr<UIMediator> mediator): UIComponent(id, mediator), checked_(false) {}void setChecked(bool checked) {checked_ = checked;std::cout << "复选框 " << id_ << " " << (checked ? "选中" : "取消选中") << std::endl;mediator_->notify(shared_from_this(), "state_changed");}bool isChecked() const { return checked_; }void trigger() override {if (enabled_) {setChecked(!checked_);}}void handleEvent(const std::string& event, const std::string& data = "") override {// 复选框对其他组件事件不敏感}
};/*** @brief 具体UI中介者*/
class ConcreteUIMediator : public UIMediator {
private:std::unordered_map<std::string, std::shared_ptr<UIComponent>> components_;public:void notify(std::shared_ptr<UIComponent> component, const std::string& event) override {std::string componentId = component->getId();if (componentId == "submit_btn" && event == "triggered") {// 提交按钮被点击:禁用所有按钮,清空文本框for (auto& pair : components_) {if (pair.first.find("btn") != std::string::npos) {pair.second->handleEvent("disable_buttons");} else if (pair.first.find("textbox") != std::string::npos) {pair.second->handleEvent("clear_text");}}} else if (componentId == "agree_checkbox" && event == "state_changed") {// 同意复选框状态变化:启用/禁用提交按钮auto checkbox = std::dynamic_pointer_cast<CheckBox>(component);if (checkbox) {auto submitBtn = components_["submit_btn"];if (submitBtn) {submitBtn->setEnabled(checkbox->isChecked());}}} else if (componentId == "name_textbox" && event == "text_changed") {// 姓名文本框内容变化:检查是否可以启用提交按钮auto textbox = std::dynamic_pointer_cast<TextBox>(component);if (textbox) {auto agreeCheckbox = components_["agree_checkbox"];auto submitBtn = components_["submit_btn"];if (agreeCheckbox && submitBtn) {bool agreed = std::dynamic_pointer_cast<CheckBox>(agreeCheckbox)->isChecked();bool hasName = !textbox->getText().empty();submitBtn->setEnabled(agreed && hasName);}}}}void registerComponent(const std::string& id, std::shared_ptr<UIComponent> component) override {components_[id] = component;std::cout << "注册UI组件: " << id << std::endl;}
};// 演示代码
int main() {// 创建UI中介者auto mediator = std::make_shared<ConcreteUIMediator>();// 创建UI组件auto nameTextbox = std::make_shared<TextBox>("name_textbox", mediator);auto agreeCheckbox = std::make_shared<CheckBox>("agree_checkbox", mediator);auto submitButton = std::make_shared<Button>("submit_btn", mediator);// 注册组件mediator->registerComponent("name_textbox", nameTextbox);mediator->registerComponent("agree_checkbox", agreeCheckbox);mediator->registerComponent("submit_btn", submitButton);std::cout << "\n=== UI交互演示 ===\n" << std::endl;// 模拟用户交互std::cout << "1. 初始状态 - 提交按钮应该被禁用" << std::endl;std::cout << "提交按钮状态: " << (submitButton->isEnabled() ? "启用" : "禁用") << std::endl;std::cout << "\n2. 输入姓名 - 提交按钮仍然被禁用(未同意条款)" << std::endl;nameTextbox->setText("张三");std::cout << "提交按钮状态: " << (submitButton->isEnabled() ? "启用" : "禁用") << std::endl;std::cout << "\n3. 同意条款 - 提交按钮应该启用" << std::endl;agreeCheckbox->trigger(); // 选中复选框std::cout << "提交按钮状态: " << (submitButton->isEnabled() ? "启用" : "禁用") << std::endl;std::cout << "\n4. 点击提交按钮 - 所有按钮被禁用,文本框被清空" << std::endl;submitButton->trigger();return 0;
}
4. 编译与运行
4.1 Makefile范例
# 编译器设置
CXX := g++
CXXFLAGS := -std=c++17 -Wall -Wextra -O2
TARGET := mediator_demo
SRCS := chat_mediator.cpp aircraft_mediator.cpp ui_mediator.cpp# 默认目标
all: $(TARGET)# 链接目标文件
$(TARGET): $(SRCS:.cpp=.o)$(CXX) $(CXXFLAGS) -o $@ $^# 编译源文件
%.o: %.cpp$(CXX) $(CXXFLAGS) -c $< -o $@# 清理生成文件
clean:rm -f *.o $(TARGET)# 运行演示
run: $(TARGET)./$(TARGET)# 调试编译
debug: CXXFLAGS += -g -DDEBUG
debug: $(TARGET).PHONY: all clean run debug
4.2 编译方法
# 1. 基础编译
make# 2. 调试模式编译
make debug# 3. 单独编译聊天室案例
g++ -std=c++17 -o chat_demo chat_mediator.cpp# 4. 清理编译文件
make clean
4.3 运行结果解读
聊天室系统输出示例:
=== 聊天开始 ===Alice 发送广播: 大家好!
=== 聊天室广播 === (Alice): 大家好!
Bob 收到来自 Alice 的消息: 大家好!
Charlie 收到来自 Alice 的消息: 大家好!Alice 私聊 Bob: 嘿,Bob,有个秘密要告诉你
=== 私聊消息 === [Alice -> Bob]: 嘿,Bob,有个秘密要告诉你
Bob 收到来自 Alice 的消息: [私聊] 嘿,Bob,有个秘密要告诉你
结果分析:
- 消息通过中介者正确路由到目标用户
- 广播消息发送给除发送者外的所有用户
- 私聊消息只发送给指定用户
- 系统消息(用户加入/离开)自动广播
5. 模式优势与局限性总结
5.1 优势对比
方面 | 使用中介者模式前 | 使用中介者模式后 |
---|---|---|
耦合度 | 对象间直接引用,网状耦合 | 通过中介者通信,星状耦合 |
可维护性 | 修改一个对象影响多个对象 | 交互逻辑集中,易于维护 |
扩展性 | 新增对象需要修改多个现有对象 | 新增对象只需注册到中介者 |
复用性 | 对象因相互引用难以单独复用 | 对象可以独立复用 |
5.2 适用场景建议
推荐使用中介者模式:
- 🎯 对象间交互复杂且频繁
- 🎯 需要集中管理对象间关系
- 🎯 系统需要良好的扩展性和维护性
不推荐使用中介者模式:
- ⚠️ 对象间交互简单直接
- ⚠️ 性能要求极高的场景
- ⚠️ 中介者可能成为系统瓶颈
中介者模式通过引入协调层,成功地将复杂的网状交互转化为清晰的星状结构,是现代软件设计中解决对象通信复杂性的重要工具。