中介者模式

中介者模式(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 的消息: 嗨,大家晚上好。
相关推荐
geovindu3 分钟前
python: Generators Pattern
开发语言·python·设计模式·生成器模式
wuminyu3 分钟前
Java锁膨胀机制之偏向锁到轻量级锁源码剖析
java·linux·c语言·jvm·c++
葱卤山猪10 分钟前
二进制字节流序列化
c++·序列化
Lazionr10 分钟前
类和对象(中):对象生命周期与运算符重载
c++
凡人叶枫14 分钟前
Effective C++ 条款13:以对象管理资源(RAII)
java·linux·开发语言·c++·嵌入式开发
星恒随风19 分钟前
C++ 类和对象入门(六):友元、内部类、匿名对象和编译器优化
开发语言·c++·笔记·学习·状态模式
Irissgwe24 分钟前
C++ STL 详解:stack 和 queue 的介绍使用与模拟实现
c++·stl·queue·stack
油炸自行车26 分钟前
【bug】Qt 6 Q_NAMESPACE 跨 DLL 链接错误:LNK2019 无法解析 staticMetaObject
数据库·c++·qt·bug·link2019·q_namespace_exp·namespaceexport
插件开发29 分钟前
英伟达cuda程序通用性关键 geforce 20xx代到最新版 在20xx上编译的c++程序可以通用吗?
java·c++·人工智能
BestOrNothing_201537 分钟前
ROS2 C++ 小车控制完整实战(三):自定义 srv 服务通信保姆级教程
c++·service通信·ros2·client·server·srv