C++设计模式之行为型模式:职责链模式(Chain of Responsibility) - 实践

news/2025/10/2 11:57:57/文章来源:https://www.cnblogs.com/ljbguanli/p/19123411

C++设计模式之行为型模式:职责链模式(Chain of Responsibility) - 实践

职责链模式(Chain of Responsibility)是行为型设计模式的一种,它通过将请求的发送者与接收者解耦,使多个接收者(处理者)组成一条链,请求在链上传递,直到被某个处理者处理。此种模式避免了请求发送者与具体处理者的直接耦合,允许动态调整处理链。

一、核心思想与角色

“就是职责链模式的核心请求沿链传递,谁能处理谁处理”,凭借构建处理者链条实现请求的分发。其核心角色如下:

角色名称核心职责
抽象处理者(Handler)定义处理请求的接口,包含一个指向后继处理者的引用,声明处理请求的方法。
具体处理者(ConcreteHandler)实现抽象处理者接口,判断自身是否能处理请求:能处理则处理,否则将请求转发给后继者。
客户端(Client)创建处理者链条,并向链的第一个处理者发送请求,无需关心具体谁处理了请求。

核心思想:将多个处理者串联成链,请求从链的起点开始传递,每个处理者自行决定是否处理请求或转发给下一个处理者,从而实现请求与处理的解耦。

二、实现示例(请假审批环境)

假设公司的请假审批流程为:

采用职责链模式可灵活构建审批流程:

#include <iostream>#include <string>// 请假请求class LeaveRequest {private:std::string employeeName; // 员工姓名int days;                 // 请假天数public:LeaveRequest(const std::string& name, int d): employeeName(name), days(d) {}std::string getEmployeeName() const { return employeeName; }int getDays() const { return days; }};// 1. 抽象处理者:审批者class Approver {protected:Approver* nextApprover; // 后继审批者(职责链的下一个节点)public:// 构造函数:初始化后继审批者Approver(Approver* next) : nextApprover(next) {}virtual ~Approver() {// 递归释放整条链(避免内存泄漏)delete nextApprover;nextApprover = nullptr;}// 纯虚方法:处理请假请求virtual void processRequest(const LeaveRequest& request) = 0;};// 2. 具体处理者1:组长(处理≤1天的请假)class TeamLeader : public Approver {public:TeamLeader(Approver* next) : Approver(next) {}void processRequest(const LeaveRequest& request) override {if (request.getDays() <= 1) {// 能处理:直接审批std::cout << "组长审批了" << request.getEmployeeName()<< "的" << request.getDays() << "天请假" << std::endl;} else if (nextApprover) {// 不能处理:转发给下一个审批者nextApprover->processRequest(request);} else {// 无后继者:无法审批std::cout << "无人能审批" << request.getEmployeeName()<< "的" << request.getDays() << "天请假" << std::endl;}}};// 2. 具体处理者2:部门经理(处理1~3天的请假)class DepartmentManager : public Approver {public:DepartmentManager(Approver* next) : Approver(next) {}void processRequest(const LeaveRequest& request) override {if (request.getDays() > 1 && request.getDays() <= 3) {std::cout << "部门经理审批了" << request.getEmployeeName()<< "的" << request.getDays() << "天请假" << std::endl;} else if (nextApprover) {nextApprover->processRequest(request);} else {std::cout << "无人能审批" << request.getEmployeeName()<< "的" << request.getDays() << "天请假" << std::endl;}}};// 2. 具体处理者3:总监(处理3~7天的请假)class Director : public Approver {public:Director(Approver* next) : Approver(next) {}void processRequest(const LeaveRequest& request) override {if (request.getDays() > 3 && request.getDays() <= 7) {std::cout << "总监审批了" << request.getEmployeeName()<< "的" << request.getDays() << "天请假" << std::endl;} else if (nextApprover) {nextApprover->processRequest(request);} else {std::cout << "无人能审批" << request.getEmployeeName()<< "的" << request.getDays() << "天请假" << std::endl;}}};// 2. 具体处理者4:总经理(处理>7天的请假)class GeneralManager : public Approver {public:GeneralManager(Approver* next) : Approver(next) {}void processRequest(const LeaveRequest& request) override {if (request.getDays() > 7) {std::cout << "总经理审批了" << request.getEmployeeName()<< "的" << request.getDays() << "天请假" << std::endl;} else if (nextApprover) {nextApprover->processRequest(request);} else {std::cout << "无人能审批" << request.getEmployeeName()<< "的" << request.getDays() << "天请假" << std::endl;}}};// 客户端代码:构建审批链并发起请求int main() {// 构建职责链:组长 → 部门经理 → 总监 → 总经理Approver* chain = new TeamLeader(new DepartmentManager(new Director(new GeneralManager(nullptr) // 链的末尾)));// 发起不同天数的请假请求LeaveRequest req1("张三", 1);   // 组长审批LeaveRequest req2("李四", 2);   // 部门经理审批LeaveRequest req3("王五", 5);   // 总监审批LeaveRequest req4("赵六", 10);  // 总经理审批LeaveRequest req5("钱七", 0);   // 组长审批(特殊情况)chain->processRequest(req1);chain->processRequest(req2);chain->processRequest(req3);chain->processRequest(req4);chain->processRequest(req5);// 释放链条(会递归释放所有处理者)delete chain;return 0;}

三、代码解析

  1. 请求对象(LeaveRequest):封装请假信息(员工姓名、天数),作为处理者的处理参数。

  2. 抽象处理者(Approver)

    • 包含nextApprover指针,指向链中的下一个处理者。
    • 声明纯虚方法processRequest(),定义处理请求的接口。
    • 析构函数递归释放整条链,避免内存泄漏。
  3. 具体处理者
    每个处理者(TeamLeaderDepartmentManager等)实现processRequest()方法,根据自身职责范围决定是否处理请求:

    • 若请求在自身处理范围内(如组长处理≤1天的请假),则直接处理。
    • 若不在范围内且存在后继处理者,则转发请求(nextApprover->processRequest(request))。
    • 若不在范围内且无后继者,则提示无法处理。
  4. 客户端构建链条
    客户端通过嵌套构造函数创建处理者链条(组长→部门经理→总监→总经理),并向链的第一个处理者发送请求,无需关心具体由谁处理。

四、核心优势与适用场景

优势
  1. 解耦请求与处理:请求发送者无需知道谁处理了请求,处理者也无需知道请求的来源,降低耦合度。
  2. 动态调整链条:可通过增删处理者或改变顺序动态调整处理流程(如临时跳过部门经理审批)。
  3. 单一职责:每个处理者只负责自己范围内的请求,符合单一职责原则。
  4. 灵活性:新处理者可随时加入链中,无需修改现有代码(符合开闭原则)。
适用场景
  1. 存在多个处理者,且处理范围明确:如审批流程、日志级别处理(DEBUG→INFO→WARN→ERROR)。
  2. 请求的处理者不确定:请求发送时不知道谁会处理,需动态决定。
  3. 应该动态调整处理流程:如根据业务规则临时改变处理顺序或增减处理步骤。

五、与其他模式的区别

模式核心差异点
职责链模式请求沿链传递,由第一个能处理的对象处理,强调“请求分发与传递”。
命令模式将请求封装为对象,可参数化、队列化请求,强调“请求的封装与执行”。
观察者模式一个对象改变时通知多个观察者,所有观察者都会处理通知,而非选择一个处理。
状态模式对象状态变化时改变行为,处理逻辑与状态绑定,而非沿链传递。

六、实践建议

  1. 明确处理范围:每个处理者的职责范围应清晰,避免重叠或遗漏导致请求无法处理。
  2. 设置默认处理者:在链的末尾添加默认处理者,确保所有请求都能被处理(或给出明确提示)。
  3. 避免过长链条:链条过长可能导致请求传递效率低,可利用分组或层级优化。
  4. 承受动态调整:设计时考虑允许运行时修改链条(如添加、移除或重新排序处理者)。

职责链模式的核心价值在于“灵活分发请求,解耦发送者与处理者”。它利用构建处理者链条,使请求能自动找到合适的处理者,同时协助动态调整处理流程,是处理具有层级关系或多步骤审批场景的理想选择。

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

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

相关文章

制作网站软件哪个好网站系统分析报告

1.字节码 1.1 什么是字节码&#xff1f; Java之所以可以“一次编译&#xff0c;到处运行”&#xff0c;一是因为JVM针对各种操作系统、平台都进行了定制&#xff0c;二是因为无论在什么平台&#xff0c;都可以编译生成固定格式的字节码&#xff08;.class文件&#xff09;供JVM…

珠海网站建设哪家好微商怎么做_和淘宝网站一样吗?

认识RBAC RBAC&#xff08;基于角色的访问控制&#xff09;是一种将权限分配给用户和服务的方法&#xff0c;基于他们的角色来确定他们可以访问和修改的资源。K8s使用RBAC作为来访请求鉴权的机制之一。 场景&#xff1a;访问K8s接口时的认证和鉴权 某些场景下&#xff0c;我…

深入解析:金融/财务图表的强大可视化引擎——Highcharts Stock

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

页面 HTTPS 化实战,从证书部署到真机验证的全流程(证书链、重定向、混合内容、抓包排查) - 实践

页面 HTTPS 化实战,从证书部署到真机验证的全流程(证书链、重定向、混合内容、抓包排查) - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block …

关于网站建设的调查报告重庆工程交易信息网

文章目录 String简单介绍常见命令应用场景 Hash简单介绍常见命令应用场景 List简单介绍常见命令应用场景 Set简单介绍常见命令应用场景 Sorted Set(Zset)简单介绍常见命令应用场景 Bitmap简单介绍常见命令应用场景 附录 Redis支持多种数据类型&#xff0c;比如String、hash、li…

路由器做网站80端口购物网站功能模块图

目录 步骤一&#xff1a;正确连接网线&#xff0c;插电开机正确连接网线&#xff1a; 认识系统灯&#xff1a; 插电开机&#xff1a; 步骤二&#xff1a;开机之后&#xff0c;系统的基本设置 1.进入设置界面&#xff1a; 2.设置辅助热点wifi&#xff1a; 3.设置日常…

网站建设哪里有wordpress的网站怎么保存

1.可放封闭原则 概念&#xff1a;一个软件实体如类、模块和函数应该对扩展开放&#xff0c;对修改关闭。即软件实体应该尽量在不修改原有代码的情况下进行扩展 2.里式替换原则 概念&#xff1a;所有引用父类的地方必须能透明地使用其子类的对象 3.依赖倒置原则 概念&#…

青岛网站建设公司外包photoshop免费素材库

一、数据安全的守护神&#xff1a;自动备份文件的重要性 在数字化时代&#xff0c;电脑中的文件承载着我们的工作成果、个人回忆以及众多重要信息。然而&#xff0c;数据丢失的风险无处不在&#xff0c;无论是硬件故障、软件崩溃&#xff0c;还是恶意软件的攻击&#xff0c;都…

AT_abc308_h [ABC308Ex] Make Q

还是比较牛的。 首先枚举一条边,钦定其中一个点,枚举这个点的出边作为 Q,然后再跑一个最小环就是结果了。 注意到此时是 \(O(n^4)\) 的,我们利用线段树分治解决 Floyd 中挖掉一个点求最短路的问题。 同样将枚举点换…

Coze源码分析-资源库-编辑插件-后端源码-核心技术与总结 - 教程

Coze源码分析-资源库-编辑插件-后端源码-核心技术与总结 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Co…

函数-高级用法+闭包

函数:增加代码可读性和重用性定义函数(3种形式): def funk() def funk(a,b) def funk(*args,**kwargs)调用函数(高级用法):1.函数做元素:2种形式 def funk1 ():print(888) def funk2():print(222) conten…

网站数据库怎么建立网站建设合同文百科

给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 206. 反转链表 - 力扣&#xff08;LeetCode&#xff09; 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 思路一 运用迭代的算法&#xff0c;把头指针…

点云-标注-分类-航线规划软件 (一)点云自动分类 - 实践

点云-标注-分类-航线规划软件 (一)点云自动分类 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas…

JVM的内存分配策略有哪些?

JVM的内存分配策略主要围绕对象在堆内存中的分配规则展开,核心目标是高效利用内存并减少垃圾回收开销。主要分配策略如下: 1. 优先在Eden区分配 大多数对象在新生代的Eden区中创建。当Eden区没有足够空间时,JVM会触…

网站的功能有哪些公司文化建设的意义

双写机制 问题的出现 在发生数据库宕机时&#xff0c;可能Innodb正在写入某个页到表中&#xff0c;但是这个页只写了一部分&#xff0c;这种情况被称为部分写失效&#xff0c;虽然innodb会先写重做日志,在修改页&#xff0c;但是重做日志中记录的是对页的物理操作&#xff0c;但…

网站网站做代理怎么发展下线太原seo优化公司

论文链接&#xff1a;http://aihuang.org/p/papers/AAAI2018Denoising.pdf来源&#xff1a;AAAI 2018MotivationDistant Supervision 是一种常用的生成关系分类训练样本的方法&#xff0c;它通过将知识库与非结构化文本对齐来自动构建大量训练样本&#xff0c;减少模型对人工标…

网站给部分文字做遮挡代码义乌外贸网站建设

2020年android 仿微信朋友圈 评论1.如果有人问我:那些艰难的岁月你是怎么熬过来的?我想我只有一句话回答:我有一种强大的精神力量支撑着我,这种力量名字叫“想死又不敢”二十、我喜欢转身转得漂亮&#xff0c;放手放得潇洒你在玩以这样的挂念&#xff0c;会悲伤的逃脱11、我嫉…

如何建设英文网站免费建站网站

HYJY系列电压继电器 HYJY-30-01集成电路电压继电器 HYJY-30-01A HYJY-30-01B HYJY-30-02集成电路电压继电器 HYJY-30-02A HYJY-30-02B HYJY-30-03-3集成电路电压继电器 HYJY-30-03-2 HYJY-30-03-1 HYJY-30-02电压继电器&#xff08;以下简称继电器&#xff09;用于发…

在Linux系统上一键配置DoH,解决DNS解析被污染

前言 最近我的 swag 服务突然证书 renew 失败 诊断了一下发现原来是无法解析 acme-v02.api.letsencrypt.org 域名 换了几个 DNS 都不行,应该是 DNS 被污染或者劫持了 这时我才意识到不上 DoH/DoT 怕是没办法了🤣 本…

免费网站源码下载器网站开发报价文件

介绍 ai查询 在Java中&#xff0c;动态数组通常通过ArrayList类来实现&#xff0c;它是Java集合框架&#xff08;Java Collections Framework&#xff09;的一部分。ArrayList是一个可调整大小的数组实现&#xff0c;提供了比标准数组更多的灵活性和功能。 以下是使用ArrayLis…