实用指南:精读C++20设计模式:行为型设计模式:中介者模式

news/2025/10/3 18:00:44/文章来源:https://www.cnblogs.com/tlnshuju/p/19124814

实用指南:精读C++20设计模式:行为型设计模式:中介者模式

2025-10-03 17:55  tlnshuju  阅读(0)  评论(0)    收藏  举报

精读C++20设计模式:行为型设计模式:中介者模式

前言

​ 中介者模式试图做的事情很简单。他跟其他行为型设计模式类似,都希望交互对象的耦合是松散的而不是紧密的。对于特别复杂的系统里,对象之间的交互会像蛛网一样纠结:A 直接调用 B,B 又调用 C,C 反过来修改 A 的状态——改动一处,波及多处。

​ 中介者模式的核心思想很简单也很优雅:把对象之间的交互搬到一个单独的“中介者”上,让对象只与中介者通信。系统由“网状耦合”变成“星形耦合”,把路由/协议/协调逻辑集中起来,降低对象之间的直接依赖。

什么是中介者模式(简短定义)

中介者模式定义了一个中介对象来封装一组对象之间的交互。中介者使各同事对象不需要显式地相互引用,从而使其耦合松散,并且可以独立地改变它们之间的交互。大家的交互更加松散起来了。每个人的接口可能都只知道一个中介者的接口,大家利用中介者交谈,中介者负责路由、规则、调度或转换消息。


示例 1 — 聊天室 Demo(最经典的例子)

​ 聊天室是大家都写过的。这里写一个超级简单迷你的版本用户(Colleague)通过 ChatRoom(Mediator)发送消息,ChatRoom 决定如何分发。

// chatroom.cpp — C++20 示例(单文件)
#include <iostream>#include <string>#include <memory>#include <unordered_map>#include <vector>struct IMediator {virtual ~IMediator() = default;virtual void send_message(const std::string& from, const std::string& to, const std::string& msg) = 0;virtual void broadcast(const std::string& from, const std::string& msg) = 0;};class User {public:User(std::string name, IMediator* mediator) : name_(std::move(name)), mediator_(mediator) {}const std::string& name() const { return name_; }void send_to(const std::string& to, const std::string& msg) {mediator_->send_message(name_, to, msg);}void send_broadcast(const std::string& msg) {mediator_->broadcast(name_, msg);}void receive(const std::string& from, const std::string& msg) {std::cout << "[" << name_ << "] 收到来自 " << from << " 的消息: " << msg << "\n";}private:std::string name_;IMediator* mediator_;};class ChatRoom : public IMediator {public:void register_user(std::shared_ptr<User> user) {users_[user->name()] = user;}void send_message(const std::string& from, const std::string& to, const std::string& msg) override {auto it = users_.find(to);if (it != users_.end()) {it->second->receive(from, msg);} else {// 可以把未找到的处理策略放在中介者中std::cout << "用户 " << to << " 不在线或不存在(由中介者处理)\n";}}void broadcast(const std::string& from, const std::string& msg) override {for (auto& [name, user] : users_) {if (name != from) user->receive(from, msg);}}private:std::unordered_map<std::string, std::shared_ptr<User>> users_;};// 使用示例int main() {ChatRoom room;auto alice = std::make_shared<User>("Alice", &room);auto bob   = std::make_shared<User>("Bob", &room);auto carol = std::make_shared<User>("Carol", &room);room.register_user(alice);room.register_user(bob);room.register_user(carol);alice->send_to("Bob", "hi Bob!");bob->send_broadcast("大家好,我是 Bob");carol->send_to("Dave", "你在哪?"); // Dave 不在线return 0;}

​ 你马上注意到,我们压根就没有出现类似:User::sendMessageTo(const User&)的签名,好像我们搞错了什么一样。相反,我们一直告诉中介者我们要给谁——User跟User的交互从复杂的网状转向了星型的结构(我们甚至可以从完全的无序网状到经典的单一中心再到分布式,但是User到User的逻辑功能丝毫不会收到任何影响)。更重要的是——任何聊天的逻辑(如是否允许私聊、是否保存日志、是否过滤敏感词等)都从User这里给剥离出去了,

  • 如果需要持久化、审计、权限检查,只需在中介者里增加对应逻辑,不必改动 User

示例 2 — 中介者 + 事件(Event-driven Mediator)

当系统更复杂时,中介者也可以作为“事件总线 / 事件路由器”来使用。事件驱动中介者将“事件类型”与“订阅者回调”解耦,支持更灵活的编排。

// event_mediator.cpp — 简化版事件中介者
#include <iostream>#include <string>#include <unordered_map>#include <vector>#include <functional>class EventMediator {public:using Handler = std::function<void(const std::string& payload)>;// 订阅某类事件(event_name)void subscribe(const std::string& event_name, Handler h) {handlers_[event_name].push_back(std::move(h));}// 发布事件,按订阅者回调投递void publish(const std::string& event_name, const std::string& payload) {auto it = handlers_.find(event_name);if (it != handlers_.end()) {for (auto &h : it->second) {h(payload);}}}private:std::unordered_map<std::string, std::vector<Handler>> handlers_;};// 使用示例int main() {EventMediator mediator;mediator.subscribe("chat.message", [](const std::string& p){std::cout << "[logger] 保存消息: " << p << "\n";});mediator.subscribe("chat.message", [](const std::string& p){std::cout << "[analytics] 增加消息计数: " << p << "\n";});mediator.publish("chat.message", "Alice -> Hello world");mediator.publish("user.login", "Bob 上线"); // 无订阅时安全忽略return 0;}

​ 事件通知就是一个经典的中介设计——事件驱动中心让各个组件的通信成为了事件的通知而不是直接的调用。

  • 事件中介者把“谁关心什么事件”作为中介者的职责,发布者和订阅者只需关心事件语义。
  • 这是构建松耦合微服务、模块间通信或者插件系统的常用做法(例如:UI 组件发出事件、由中介者分发给需要的服务)。

示例 3 — 中介者的“服务热线”(智能路由 / 负载分配)

把中介者想成“调度员”会很直观:例如客服系统里,用户来电由中介者(路由器)分配到空闲或最适合的客服代表(agent)。把决策逻辑集中到中介者中,可以灵活策略化(轮询、优先级、技能匹配、加权等)。

// hotline_mediator.cpp — 简化热线调度
#include <iostream>#include <string>#include <vector>#include <optional>#include <functional>struct Agent {std::string name;bool available = true;std::vector<std::string> skills; // 如 { "billing", "technical" }void handle(const std::string& user, const std::string& issue) {available = false;std::cout << "[" << name << "] 处理用户 " << user << " 的问题: " << issue << "\n";// 处理结束后设为可用(示例中直接释放)available = true;}};class HotlineMediator {public:void add_agent(Agent a) { agents_.push_back(std::move(a)); }// 简单策略:按技能优先匹配,再轮询就近分配void route_call(const std::string& user, const std::string& issue, const std::string& required_skill="") {// 1. 优先找能处理该技能且可用的for (auto &ag : agents_) {if (ag.available && (required_skill.empty() || has_skill(ag, required_skill))) {ag.handle(user, issue);return;}}// 2. 找任何可用的for (auto &ag : agents_) {if (ag.available) {ag.handle(user, issue);return;}}// 3. 全部忙,放入队列或提示等待(中介者负责排队)std::cout << "所有客服均忙,请稍后重试或排队(由中介者在此处实现队列逻辑)\n";}private:bool has_skill(const Agent& a, const std::string& s) {for (auto &k : a.skills) if (k == s) return true;return false;}std::vector<Agent> agents_;};// 使用示例int main() {HotlineMediator mediator;mediator.add_agent(Agent{"Alice", true, {"billing", "general"}});mediator.add_agent(Agent{"Bob", true, {"technical"}});mediator.add_agent(Agent{"Carol", false, {"general"}}); // 暂不可用mediator.route_call("用户001", "无法支付,扣款失败", "billing");mediator.route_call("用户002", "APP 崩溃", "technical");mediator.route_call("用户003", "想了解套餐"); // 没指定技能 -> 任意可用return 0;}

​ Hotline 中介者把“谁来接、按什么规则接”放在一处。以后如果想改策略(按技能匹配权重、加入优先级、按SLA分配、或把请求发给外包),只需修改中介者即可,不必更改 Agent 或 Caller 的实现。


总结

我们试图解决什么问题?
  1. 对象之间耦合过高:每个对象都持有其他对象的引用并直接调用,导致改动传播、测试困难。
  2. 交互逻辑散落在多个类中:路由、过滤、权限、日志、转换等逻辑散落在同事对象,使得职责不清晰。
  3. 难以统一变更/策略化:当交互规则变更(例如需要增加日志或限流)时,必须在多个地方修改代码。
  4. 复杂的协调/调度需求:例如多方协商、优先级处理、并发控制等,逐一放在每个对象会非常混乱。

我们如何解决问题?
  • 引入中介者(Mediator)对象:所有需要交互的同事对象不再直接互相调用,而是通过中介者发送/接收消息或事件。
  • 集中化交互逻辑:中介者负责路由决策、数据转换、权限校验、队列/重试、审计日志等。
  • 支持多种风格:中介者既可以是简单的“发-收”桥(如聊天室),也可以是事件总线(publish/subscribe),或是智能调度器(热线)。
  • 策略化与扩展:中介者内部的调度/路由策略可以抽象成策略对象或插件,使得行为可配置、可替换。

优点与缺点分析
优点
  1. 降低类之间的耦合度:同事对象只依赖中介者接口,易于替换与测试。
  2. 集中管理复杂逻辑:路由、权限、审计等逻辑在一个地方实现,维护性更好。
  3. 更方便演进与扩展:新规则、新功能(如限流、备份路由)只需要改中介者或新增中介者策略。
  4. 便于实现跨模块的横切功能:如记录、监控、异常处理等可在中介者层统一处理。
缺点(以及如何缓解)
  1. 中介者可能变成“上帝对象”:把太多逻辑塞进去会导致中介者复杂臃肿。
    • 缓解:把中介者内部拆成多个策略/处理链(Chain of Responsibility),或拆出多个中介者(按领域划分)。
  2. 增加单点责任:中介者成为交互路径的关键,若中介者出问题会影响整个系统。
    • 缓解:中介者自身要设计成高可用、可替换(接口化、支持备份/降级)。
  3. 性能开销:中介者增加了一层转发,极端低延迟场景可能需权衡。
    • 缓解:在高性能场景可以采用直接调用 + 中介者用于非关键路径,或者让中介者做并发优化(批量转发、零拷贝等)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/926157.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c 网站开发引擎wordpress 后台翻译

一 前言MySQL 5.7.8 之后 支持 JSON (由rfc7159规定)数据类型&#xff0c;其能在字段中使用json 类型&#xff0c;做到了自动校验是否为json类型数据&#xff0c;否则插入数据会报异常&#xff1b;其次&#xff0c;储存json数据内部做到了优化储存&#xff0c;能够快速读取json…

成都医院手机网站建设宁波网站推广优化外包公司

动动发财的小手&#xff0c;点个赞吧&#xff01; 简介 作为理解、生成和处理自然语言文本的有效方法&#xff0c;自然语言处理&#xff08;NLP&#xff09;的研究近年来呈现出快速传播和广泛采用。鉴于 NLP 的快速发展&#xff0c;获得该领域的概述并对其进行维护是很困难的。…

查看cuda型号.

查看cuda型号.cmd打开命令行:执行命令:nvidia-smi这里展示CUDA的版本号是与当前 GPU 驱动(driver)程序兼容的 CUDA 运行时版本。这是驱动程序支持的最高 CUDA 版本,并不是系统上安装的 CUDA的版本!!!!!!!!!!!!!!!…

AI元人文系列:透明推理者——下一代大模型架构设计

AI元人文系列:透明推理者——下一代大模型架构设计 引言:从“智能工具”到“思维伙伴” 人工智能正站在新的十字路口。当前的大模型能够创作诗歌、解答难题、生成代码,却无法清晰回答一个简单却关键的问题:“你为什…

个人随笔

设计网页时,如果遇到float 一定要注意下面的元素要 clear,不然会有bug

专做电器的网站怎么开网站做站长

利用图扑三维可视化技术展示园区在不同时间段的变化&#xff0c;提供全景漫游体验&#xff0c;帮助用户全方位感受和理解园区环境&#xff0c;实现智能化管理与优化。

Fedora Atomic Desktops

https://docs.fedoraproject.org/en-US/emerging/ Fedora Atomic DesktopsFedora Silverblue Fedora Silverblue is an atomic desktop operating system featuring the GNOME desktop, a beautiful, high-quality des…

完整教程:【论文阅读】具身人工智能:从大型语言模型到世界模型

完整教程:【论文阅读】具身人工智能:从大型语言模型到世界模型pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "C…

Android达成RecyclerView粘性头部效果,模拟微信账单列表的月份标题平移

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

实用指南:【C语言】char * 、char [ ]、const char * 和 void *的使用以及区别

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

常德网站优化公司东莞大岭山天气预报

【我是谁】 1.学历&#xff1a;22届双非本科校企合作&#xff08;软外&#xff0c;软件工程服务外包&#xff09;&#xff0c;编程课大部分是印度的NIIT老师上课&#xff0c;印式英语一点儿听不懂。。。所以大学全都自学的&#xff0c;和非科班的也没什么区别和优势&#xff0c…

PowerShell注意点

$()和${}的区别: $()表示命令替换,将括号内的命令执行后得到的输出作为值。 例如,$(ls)将会执行ls命令后得到当前目录下的文件列表作为值。 ${}表示变量替换,将大括号内的变量的值作为值。 例如,${a}将取变量a的值…

自动化脚本的自动化执行实践 - 详解

自动化脚本的自动化执行实践 - 详解2025-10-03 17:36 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !imp…

做商业网站的服务费维护费直播型网站开发

在Kotlin中&#xff0c;注解&#xff08;Annotations&#xff09;是一种用于在程序代码中添加元数据的特殊标记。它们提供了对代码的描述性信息&#xff0c;但本身并不会影响程序的运行。注解可以应用于类、方法、属性等程序元素上&#xff0c;用于提供关于这些元素的额外信息。…

m3u8转mp4软件中文版推荐与使用指南

近年来,随着在线视频的普及,m3u8格式的流媒体文件变得越来越常见。不少用户希望将m3u8文件转换为通用的mp4格式,便于本地保存、播放或分享。那么,选择一款好用的m3u8转mp4软件中文版,就成了很多小伙伴的需求。下面…

Unity简易事件分发器

一、EventFunctionusing System; namespace EventCore {public struct EventFunction{public object _caller;public Action _action;}public struct EventFunction<T>{public object _caller;public Action<…

react怎么做pc网站外贸soho建站

本文给大家整理了腾讯视频网页下载_腾讯视频怎么下载视频方面的内容。腾讯视频独播剧质量还是可以的&#xff0c;比较给力的是腾讯视频大量买入了老剧的版权&#xff0c;不乏一些比较经典的港剧&#xff0c;还把这些老剧修复了。腾讯视频播放器是一款支持多种音视频格式的主流播…

实用指南:1、docker入门简介

实用指南:1、docker入门简介pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco"…

调试parlant的大模型配置,最终自己动手写了g4f的模块挂载 - 教程

调试parlant的大模型配置,最终自己动手写了g4f的模块挂载 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "…

网站模板如何使用 如何修改吗网站视频插件

合并分支用rebase还是merge&#xff1f; 实际开发工作的时候&#xff0c;我们都是在自己的分支开发&#xff0c;然后将自己的分合并到主分支&#xff0c;那合并分支用2种操作&#xff0c;这2种操作有什么区别呢&#xff1f; git上新建一个项目&#xff0c;默认是有master分支…