c++ 职责链+策略模式,实现链式过程处理

想象一下一件商品的加工,需要经过多道工艺流程处理。这一道道工艺流程就像是一个职责链,从头到尾传递。就好像网络传输时,消息发送到对端主机,经过协议栈,在这个过程中,它在不同的协议栈层级中被一层层抽丝剥茧,剥掉ip头,脱下tcp头,扯掉http头。又像清丽的舞娘不再犹抱琵琶,缓缓揭开自己面纱,犹如李清照《一剪梅》中的“轻解罗裳,独上兰舟”,最后对端的层得到了自己想要的东西。

(注:纯知识总结分享,示例和业务内容虚构)

前言

假设一个常见的应用场景。在公司申请工作流程,你的申请就会在一条流中传递,这条流程从开始提交申请,到你的经理,最终是最后部门的任务接收者。这些个对象每个对象都能审批和转交下一步。这就像是一条职责链。同时随着需求的变动,又可能需要变换或增加新的处理者,类似这种采用策略模式+职责链就很合适了。

职责链(Chain of Responsibility)模式也许被看做一个使用策略对象的“递归的动态一般化".此时发起一个调用,在一个链序列中的每个策略都试图满足这个调用。这个过程直到有一个策略成功满足该调用或者到达序列的末尾才结束。通过遍历策略链,调用函数的不同实现,直至达到某个终止条件到达最后的处理。

策略模式

策略模式(Strategy):它定义了算法家族,分别放封装起来,让它们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户。策略是一个接口,该接口定义若干个算法标识,即定义了若干个抽象方法。

在策略模式中,我们定义一系列算法,并将它们封装在独立的类中,这些类实现了一个共同的接口。然后,我们可以在运行时选择一个具体的算法类来执行特定的行为。

对于Strategy模式来说,主要有如下优点:

1、  提供了一种替代继承的方法,而且既保持了继承的优点(代码重用)还比继承更灵活(算法独立,可以任意扩展)。

2、  避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。

3、  遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。

以下是一个使用策略模式的示例:

#include <iostream>// 策略接口
class Strategy {
public:virtual void execute() = 0;
};// 具体策略类1
class ConcreteStrategy1 : public Strategy {
public:void execute() override {std::cout << "Executing strategy 1" << std::endl;}
};// 具体策略类2
class ConcreteStrategy2 : public Strategy {
public:void execute() override {std::cout << "Executing strategy 2" << std::endl;}
};// 上下文类
class Context {
private:Strategy* strategy;public:Context(Strategy* strategy) : strategy(strategy) {}void setStrategy(Strategy* strategy) {this->strategy = strategy;}void executeStrategy() {strategy->execute();}
};int main() {// 创建具体策略对象ConcreteStrategy1 strategy1;ConcreteStrategy2 strategy2;// 创建上下文对象并设置初始策略Context context(&strategy1);// 执行初始策略context.executeStrategy();// 切换策略并执行context.setStrategy(&strategy2);context.executeStrategy();return 0;
}

在上面的示例中,我们定义了一个`Strategy`接口,它有一个`execute()`方法。然后,我们创建了两个具体的策略类`ConcreteStrategy1`和`ConcreteStrategy2`,它们分别实现了`execute()`方法。

接下来,我们定义了一个上下文类`Context`,它有一个指向`Strategy`接口的指针成员变量。在构造函数中,我们传入一个初始的策略对象,并在`executeStrategy()`方法中调用该策略对象的`execute()`方法。

在`main()`函数中,我们创建了具体的策略对象,并将其传递给上下文对象。然后,我们调用上下文对象的`executeStrategy()`方法来执行策略。

通过使用策略模式,我们可以在运行时动态地选择不同的策略,并且可以轻松地添加新的策略类。这使得我们的代码更加灵活和可扩展。

职责链模式

在GOF的《设计模式:可复用面向对象软件的基础》一书中对职责链模式是这样说的:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,直到有一个对象处理它为止。在职责链模式中,每个处理者都包含一个对下一个处理者的引用,当一个请求到达时,首先由第一个处理者处理,如果该处理者能够处理该请求,则处理结束,否则将请求传递给下一个处理者,直到请求被处理或者到达链的末尾。

优缺点

1、降低耦合度;职责链模式使得一个对象不用知道是哪一个对象处理它的请求。对象仅需要知道该请求会被正确的处理。接收者和发送者都没有对方的明确的信息,且链中的对象不需要知道链的结构;

2、增强了给对象指派职责的灵活性;当在对象中分派职责时,职责链给你更多的灵活性。你可以通过在运行时对该链进行动态的增加或修改来增加或改变处理一个请求的那些职责;


3、不保证被接受,既然一个请求没有明确的接收者,那么就不能保证它一定会被处理;该请求可能一直到链的末端都得不到处理。一个请求也可能因该链没有被正确配置而得不到处理。

以下是一个简单的C++职责链模式的示例:

#include <iostream>
using namespace std;// 抽象处理者
class Handler {
public:virtual void setNext(Handler* next) = 0;virtual void handleRequest(int request) = 0;
};// 具体处理者A
class ConcreteHandlerA : public Handler {
private:Handler* next;
public:void setNext(Handler* next) {this->next = next;}void handleRequest(int request) {if (request >= 0 && request < 10) {cout << "ConcreteHandlerA处理请求:" << request << endl;}else if (next != nullptr) {next->handleRequest(request);}}
};// 具体处理者B
class ConcreteHandlerB : public Handler {
private:Handler* next;
public:void setNext(Handler* next) {this->next = next;}void handleRequest(int request) {if (request >= 10 && request < 20) {cout << "ConcreteHandlerB处理请求:" << request << endl;}else if (next != nullptr) {next->handleRequest(request);}}
};// 具体处理者C
class ConcreteHandlerC : public Handler {
private:Handler* next;
public:void setNext(Handler* next) {this->next = next;}void handleRequest(int request) {if (request >= 20 && request < 30) {cout << "ConcreteHandlerC处理请求:" << request << endl;}else if (next != nullptr) {next->handleRequest(request);}}
};int main() {// 创建处理者对象Handler* handlerA = new ConcreteHandlerA();Handler* handlerB = new ConcreteHandlerB();Handler* handlerC = new ConcreteHandlerC();// 设置处理者的下一个处理者handlerA->setNext(handlerB);handlerB->setNext(handlerC);// 发起请求handlerA->handleRequest(5);handlerA->handleRequest(15);handlerA->handleRequest(25);// 释放内存delete handlerA;delete handlerB;delete handlerC;return 0;
}

具体实现举例

以控制机器导轨上的滑块儿(slider)为例,若想让滑块儿按需求完成准确的动作,需经过参数校正、速度限制、算法补偿等一系列的加工处理,最后下发给控制器响应。如果不经过好的设计,每次增加新的功能都无法很好的维护和拓展。而采用职责链和策略模式是一种好的处理方式。比如滑块儿的运动可以像工厂流水线一样经过一系列的加工处理(职责链模式)。

先定义一下该滑块儿所具备的基础动作功能策略接口:

class SliderIFA
{
public:SliderIFA() = default;virtual ~SliderIFA() = default;public:// 按指定位置移动virtual void tMove() = 0;// 按步进方式移动virtual void iMove() = 0;virtual void enable() = 0;// ......
}

接下来实现不同的技能(策略),如基本技能,慢动作技能,快动作技能,花样动作技能,采用策略模式和责任链模式的组合,分别给予实现,实现同一策略动作的加工处理。

定义一个父类BaseIFA继承自策略接口SliderIFA ,先实现一种策略作为公共类用,内部实现职责链模式。而且定义这样一个父类还有个好处是,如果策略接口有很多,继承自该父类的策略类,不用实现所有的策略接口,只需重载实现需要的接口即可。其他未重载的接口则自动执行父类中的方法。 

class BaseIFA : public SliderIFA
{
public:BaseIFA() {}public:void setNextStrategy(std::shared_ptr<SliderIFA> coreIfa) { nextStrategy_ = std::move(coreIfa); }public:void tMove() override;void iMove() override;void enable() override;protected:std::shared_ptr<SliderIFA> nextStrategy_;}
void BaseIFA::enable()
{if (nextStrategy_){return nextStrategy_->enable();}
}void BaseIFA::tMove()
{if (nextStrategy_){return nextStrategy_->tMove();}
}void BaseIFA::iMove()
{if (nextStrategy_){return nextStrategy_->iMove();}
}

举例,责任者一的实现:

class Hander1:public BaseIFA
{
public:Hander1() {}public:void tMove() override;void iMove() override;void enable() override;
}void Hander1::enable()
{if (nextStrategy_){return nextStrategy_->enable();}
}void Hander1::tMove()
{if (nextStrategy_){return nextStrategy_->tMove();}
}void Hander1::iMove()
{if (nextStrategy_){return nextStrategy_->iMove();}
}

责任者二的实现:

class Hander2:public BaseIFA
{
public:Hander2() {}public:void tMove() override;void iMove() override;void enable() override;
}void Hander2::enable()
{if (nextStrategy_){return nextStrategy_->enable();}
}void Hander2::tMove()
{if (nextStrategy_){return nextStrategy_->tMove();}
}void Hander2::iMove()
{if (nextStrategy_){return nextStrategy_->iMove();}
}

最终使用如下所示,每个hander都是一种策略接口的实现,然后按照责任链模式链式处理。比如可以按照3->1->2的加工顺序,或者按照1->2->3的加工顺序处理。或者只经过1和2这两道加工工序,实现效果:

int main()
{std::shared_ptr<SliderIFA> core_ = nullptr;auto hander1_ = std::make_shared<Hander1>();auto hander2_ = std::make_shared<Hander2>();auto hander3_ = std::make_shared<Hander3>();// 举例,实现1->2->3的排列组合链式处理hander1_->setNextStrategy(hander2_ );hander2_->setNextStrategy(hander3_ );core_ = std::move(hander1_); //入口// 执行core_->tMove();return 0;
} 

最终的一个tMove接口会先经过hander1->hander2->hander3的链式加工处理,达到的想要的效果。且可以任意的排列组合,hander1->hander3也是可以的。

优化改进,为了保持BaseIFA这个基类的简洁直观,可以单独整一个默认的策略类,作为最终职责链最后一级的处理,接口默认实现都写在这里。这样只需定一个DefaultHander继承自策略接口SliderIFA,在BaseIFA基类中持有它的引用即可。如:

class BaseIFA : public SliderIFA
{
public:explicit BaseIFA(std::shared_ptr<DefaultHander> coreImp) : coreImp_(std::move(coreImp)) {}public:void setNextStrategy(std::shared_ptr<SliderIFA> coreIfa) { nextStrategy_ = std::move(coreIfa); }public:void tMove() override;void iMove() override;void enable() override;protected:std::shared_ptr<SliderIFA> nextStrategy_;std::shared_ptr<DefaultHander> coreImp_;
}void BaseIFA::enable()
{if (nextStrategy_){return nextStrategy_->enable();}if (core_){return core_->enable();}
}void BaseIFA::tMove()
{if (nextStrategy_){return nextStrategy_->tMove();}if (core_){return core_->tMove();}
}void BaseIFA::iMove()
{if (nextStrategy_){return nextStrategy_->iMove();}if (core_){return core_->iMove();}
}class DefalutHander: public SliderIFA
{
public:BaseIFA() {}public:void tMove() override;void iMove() override;void enable() override;}class Hander1:public BaseIFA
{
public:explicit Hander1(std::shared_ptr<DefaultHander> core_) {}public:void tMove() override;void iMove() override;void enable() override;
}void Hander1::enable()
{if (nextStrategy_){return nextStrategy_->enable();}if (core_){return core_->enable();}
}void Hander1::tMove()
{if (nextStrategy_){return nextStrategy_->tMove();}if (core_){return core_->tMove();}
}void Hander1::iMove()
{if (nextStrategy_){return nextStrategy_->iMove();}if (core_){return core_->iMove();}
}int main()
{std::shared_ptr<SliderIFA> core_ = nullptr;//默认策略类auto default_ = std::make_shared<DefaultHander>();//策略实现一auto hander1_ = std::make_shared<Hander1>(default_ );//策略实现二auto hander2_ = std::make_shared<Hander2>(default_ );//策略实现三auto hander3_ = std::make_shared<Hander3>(default_ );// 举例,实现1->2->3的排列组合链式处理hander1_->setNextStrategy(hander2_ );hander2_->setNextStrategy(hander3_ );core_ = std::move(hander1_); //入口// 执行core_->tMove();return 0;
} 

其他资源

c++ 策略模式_每木昔月的博客-CSDN博客_c++策略模式

C++常用设计模式

模式和原则 [原] - Justin - 博客园

设计模式随笔系列:鸭子-策略模式(Strategy)[原] - Justin - 博客园

c++手撕代码(六)设计模式:观察者,策略,责任链, 装饰器 - 知乎

Java设计模式——策略模式_塔塔开!!!的博客-CSDN博客_策略模式

C++设计模式——职责链模式_静思心远的博客-CSDN博客_c++设计模式职责链

C++设计模式-策略模式(Strategy)基本轮廓_IT1995的博客-CSDN博客_c++ 策略模式

设计模式(C++实现)(二十一)——职责链模式_huashuolin001的博客-CSDN博客

c++设计模式(七) 职责链模式:尝试采用一系列策略模式 - Just enjoy programming - C++博客

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

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

相关文章

《实战AI模型》——赶上GPT3.5的大模型LLaMA 2可免费商用,内含中文模型推理和微调解决方案

目录 准备环境及命令后参数导入: 导入模型: 准备LoRA: 导入datasets: 配置Config:

C# 存在重复元素

217 存在重复元素 给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 &#xff0c;返回 true &#xff1b;如果数组中每个元素互不相同&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3,1] 输出&#xff1a;true 示例 2&#xff1a; 输…

从小白到大神之路之学习运维第67天-------Tomcat应用服务 WEB服务

第三阶段基础 时 间&#xff1a;2023年7月25日 参加人&#xff1a;全班人员 内 容&#xff1a; Tomcat应用服务 WEB服务 目录 一、中间件产品介绍 二、Tomcat软件简介 三、Tomcat应用场景 四、安装配置Tomcat 五、配置目录及文件说明 &#xff08;一&#xff09;to…

docker版jxTMS使用指南:站点的调整

本文讲解4.4版jxTMS中的站点的调整&#xff0c;整个系列的文章请查看&#xff1a;[docker版jxTMS使用指南&#xff1a;docker版jxTMS使用指南&#xff1a;4.4版升级内容 docker版本的使用&#xff0c;请查看&#xff1a;docker版jxTMS使用指南 4.0版jxTMS的说明&#xff0c;请…

zabbix钉钉报警

登录钉钉客户端,创建一个群,把需要收到报警信息的人员都拉到这个群内. 然后点击群右上角 的"群机器人"->"添加机器人"->"自定义", 记录该机器人的webhook值。 添加机器人 在钉钉群中&#xff0c;找到只能群助手 添加机器人 选择自定义机…

【玩转Linux】文件的一些概念

(꒪ꇴ꒪ ),hello我是祐言博客主页&#xff1a;C语言基础,Linux基础,软件配置领域博主&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff01;送给读者的一句鸡汤&#x1f914;&#xff1a;集中起来的意志可以击穿顽石!作者水平很有限&#xff0c;如果发现错误&#x…

怎样接入chatGPT

官网链接&#xff1a; OpenAI platform

Unity Coroutine 协程

Unity Coroutine 是一种在 Unity 引擎中实现异步编程的技术&#xff0c;它可以让你在多个帧中分段执行一个函数&#xff0c;而不是一次性地执行完毕。Coroutine 可以使用 yield 语句来暂停和恢复执行&#xff0c;从而实现一些复杂的逻辑和效果&#xff0c;如时间延迟、循环动画…

UE4/5C++多线程插件制作(十四、MTPAbandonable)

目录 MTPAbandonable h实现 cpp实现 MTPMarco.h 首先是异步任务的宏定义部分:

IDEA+SpringBoot +ssm+ Mybatis+easyui+Mysql求职招聘管理系统网站

IDEASpringBoot ssm MybatiseasyuiMysql求职招聘管理系统网站 一、系统介绍1.环境配置 二、系统展示1. 登录2.注册3.首页4.公司5.关于我们6.我的简历7.我投递的简历8.修改密码9. 管理员登录10.我的信息11.用户信息12.职位类别13. 职位列表14. 公司列表15. 日志列表 三、部分代码…

【Rust 基础篇】Rust 封装

导言 在 Rust 中,封装是一种面向对象编程的重要概念,它允许将数据和相关的方法组合在一起,形成一个独立的单元。通过封装,我们可以隐藏数据的实现细节,只暴露需要对外部使用的接口,从而提高代码的可维护性和安全性。本篇博客将详细介绍 Rust 中封装的概念,包含代码示例…

Visual Studio 2022 cmake配置opencv开发环境

1. 环境与说明 这里我用的是 widnows 10 64位&#xff0c;Visual Studio 用的 Visual Studio Community 2022 (社区版) 对于Android开发工程师来说&#xff0c;为什么要使用Visual Studio 呢 ? 因为在Visual Studio中开发调试OpenCV方便&#xff0c;可以开发调试好后&#xf…

Transformer+医学图像最新进展【2023】

Transformer主要用于自然语言处理领域。近年来,它在计算机视觉(CV)领域得到了广泛的应用。医学图像分析(MIA,Medical image analysis)作为机器视觉(CV,Computer Vision)的一个重要分支,也极大地受益于这一最先进的技术。 机构:新加坡国立大学机械工程系、中山大学智能系…

基于Eisvogel模板的Markdown导出PDF方法

Requirements 模板地址&#xff1a;Wandmalfarbe/pandoc-latex-template Pandoc&#xff1a;Pandoc官网 Latex环境&#xff1a;例如TexLive Pandoc参数 --template"模板存放位置" --listings --pdf-enginexelatex --highlight-style kate -V CJKmainfontSimSun -V C…

安装nvm管理node

1.首先卸载已存在的node cmd输入node提示&#xff1a;‘node’ 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。 2.安装nvm 安装地址 &#xff1a;Releases coreybutler/nvm-windows GitHub 本人选择1.1.7 输入nvm打印出了nvm的指令列表&#xff0c;说明…

SpringBoot整合JDBC

1. 创建一个新的Spring Boot项目或者使用现有的Spring Boot项目作为基础 2. 在项目的pom.xml文件中添加Spring Boot JDBC和MySQL依赖 打开pom.xml文件&#xff0c;添加以下依赖: <dependency><groupId>org.springframework.boot</groupId><artifactI…

IDEA+SpringBoot + Mybatis + Shiro+Bootstrap+Mysql资产设备管理系统

IDEASpringBoot Mybatis ShiroBootstrapMysql资产设备管理系统 一、系统介绍1.环境配置 二、系统展示1. 管理员登录2.用户新增3.用户设置4.岗位管理5. 审批节点6. 人员查询7. 组织设置8. 人员调整9.角色设置10.角色模块映射11.模块设置12.应用模块13.光纤交换机14.服务器15.网…

C++之文件操作

1.C文件操作 C中文件操作头文件:fstream。   文件类型&#xff1a;文件文件和二进制文件。 文件操作三大类&#xff1a;     ofstream 写操作     ifstream 读操作     fstream:读写操作 文件打开方式&#xff1a; 标志说明ios::in只读ios::out只写,文件不存在则…

大数据面试题:HBase读写数据流程

面试题来源&#xff1a; 《大数据面试题 V4.0》 大数据面试题V3.0&#xff0c;523道题&#xff0c;679页&#xff0c;46w字 参考答案&#xff1a; 1、写数据流程 1&#xff09;Client先访问zookeeper&#xff0c;获取hbase:meta表位于哪个Region Server。 2&#xff09;访…

SQL 常见函数整理 _ LTRIM/RTRIM (去除字符串首尾空字符)

LTRIM() 1. 用法 用于去除字符串左侧&#xff08;开头&#xff09;的空格或指定的字符。它返回一个新的字符串&#xff0c;其中左侧的空格或指定字符被删除。 2. 基本语法 LTRIM ( string )参数说明&#xff1a; string&#xff1a;必需&#xff0c;要去除空格或指定字符的字…