中介者模式

中介者模式(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)

​​​​​​​ChatRoomUser 互相持有对方。如果都用 shared_ptr,会造成循环引用 ,导致内存泄漏。解决方案:ChatRoom 持有 Usershared_ptr(拥有权),而 User 持有 ChatRoomweak_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 的消息: 嗨,大家晚上好。
相关推荐
crescent_悦2 小时前
PTA C++:正整数A+B
数据结构·c++·算法
YYYing.2 小时前
【Linux/C++多线程篇(一) 】多线程编程入门:从核心概念到常用函数详解
linux·开发语言·c++·笔记·ubuntu
一起搞IT吧2 小时前
Android功耗系列专题理论之十六:功耗不同阶段&不同模块分析说明
android·c++·智能手机·性能优化
bmseven2 小时前
大白话讲解23种设计模式简介
设计模式
蜜獾云2 小时前
设计模式之代理模式:本地接口代理远程接口的调用
设计模式·系统安全·代理模式
荣光属于凯撒2 小时前
P15755 [JAG 2025 Summer Camp #1] JAG Box
c++·算法·贪心算法
蜜獾云2 小时前
设计模式之访问者模式:动态的给目标对象增加新功能
设计模式·访问者模式
郝学胜-神的一滴2 小时前
CMake:解锁C++跨平台工程构建的核心密钥
开发语言·c++·职场和发展
佑白雪乐2 小时前
C++标准总结+VSCode使用+MinGW
开发语言·c++·vscode