突破编程_C++_设计模式(策略模式)
1 策略模式的概念
策略模式(Strategy Pattern)是 C++ 中常用的一种行为设计模式,它能在运行时改变对象的行为。在策略模式中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为模式。
在策略模式中,需要创建表示各种策略的对象和一个行为随着策略对象改变而改变的 Context 对象。策略对象更改 Context 对象的执行算法。
在策略模式中,通常包括以下几个角色:
(1)策略接口(Strategy Interface): 定义了一个策略的公共接口,所有具体的策略类都需要实现这个接口。这个接口声明了策略对象将执行的操作。
(2)具体策略类(Concrete Strategy Classes): 实现了策略接口,提供了具体的算法或行为。每个具体策略类都封装了实现特定行为或算法的代码。
(3)上下文(Context): 维护一个指向策略对象的引用,并定义一个接口来让策略对象执行其算法。上下文类并不知道具体的策略类,它只知道策略接口。这样,上下文可以将请求转发给当前关联的策略对象来执行。
2 策略模式的实现步骤
在 C++ 实现策略模式的实现步骤如下:
(1)定义策略接口:
首先,需要定义一个策略接口。这个接口通常是一个纯虚类,声明了一组公共的、需要由具体策略类来实现的方法。这些方法是策略对象将执行的行为的抽象描述。
(2)实现具体策略类:
接下来,创建实现策略接口的具体策略类。每个具体策略类都包含了实现特定算法或行为的代码。这些类继承了策略接口,并实现了接口中声明的所有方法。
(3)创建上下文类:
上下文类负责维护对策略对象的引用,并定义了一个接口,以便客户端代码可以通过这个接口来执行策略对象的方法。上下文类通常包含一个指向策略接口的指针或引用,并通过这个指针或引用来调用策略方法。上下文类本身并不关心具体使用了哪个策略,它只关心策略接口。
(4)在上下文中设置策略对象:
在客户端代码中,创建具体策略类的对象,并将其传递给上下文对象。上下文对象使用这个策略对象来执行相应的算法或行为。客户端代码可以通过调用上下文类的方法来间接调用策略对象的方法。
(5)执行策略:
客户端代码通过调用上下文类的执行方法(例如 executeStrategy()),来触发策略的执行。上下文类将调用当前设置的策略对象的方法,实现相应的算法或行为。
(6)更改策略:
如果需要改变行为,客户端代码可以创建另一个策略对象,并将其设置为上下文对象的新策略。这样,上下文对象在执行策略时会使用新的算法或行为。
通过这些步骤,策略模式允许在运行时动态地改变对象的行为,提高了代码的灵活性和可维护性。它通过将算法和行为封装在独立的策略类中,实现了算法与使用算法的客户端代码之间的解耦。这样,客户端代码只需关注于如何使用策略,而不需要关心策略的具体实现。
如下为样例代码:
#include <iostream>
#include <memory>// 步骤1: 定义策略接口
class Strategy {
public:virtual ~Strategy() {}virtual void execute() = 0;
};// 步骤2: 实现具体策略类
class ConcreteStrategyA : public Strategy {
public:void execute() override {std::cout << "Executing strategy A" << std::endl;}
};class ConcreteStrategyB : public Strategy {
public:void execute() override {std::cout << "Executing strategy B" << std::endl;}
};// 步骤3: 创建上下文类
class Context {
public:// 通过构造函数设置策略 Context(std::unique_ptr<Strategy> strategy) : strategy(std::move(strategy)) {}// 执行策略 void executeStrategy() {if (strategy) {strategy->execute();}else {std::cout << "No strategy is set." << std::endl;}}// 更改策略 void setStrategy(std::unique_ptr<Strategy> newStrategy) {strategy = std::move(newStrategy);}private:std::unique_ptr<Strategy> strategy; // 使用unique_ptr管理策略对象的生命周期
};// 步骤4-6: 在客户端代码中设置和执行策略
int main()
{// 创建具体策略对象并使用unique_ptr管理 auto strategyA = std::make_unique<ConcreteStrategyA>();auto strategyB = std::make_unique<ConcreteStrategyB>();// 创建上下文对象并设置初始策略 Context context(std::move(strategyA));context.executeStrategy(); // 输出:Executing strategy A // 更改策略 context.setStrategy(std::move(strategyB));context.executeStrategy(); // 输出:Executing strategy B // 此时strategyA和strategyB已经被unique_ptr自动释放 return 0;
}
上面代码的输出为:
Executing strategy A
Executing strategy B
在上面代码中,Strategy 是策略接口,ConcreteStrategyA 和 ConcreteStrategyB 是两个实现了该接口的具体策略类。Context 类使用 std::unique_ptr 来管理策略对象的生命周期,确保在不再需要时能够自动释放资源。
在 main 函数中,创建了两个 std::unique_ptr<Strategy> 对象,分别指向 ConcreteStrategyA 和 ConcreteStrategyB 的实例。然后将这些智能指针传递给 Context 对象,并通过 setStrategy 方法来更改策略。每次调用 executeStrategy 方法时,Context 对象都会执行当前设置的策略。
3 策略模式的应用场景
C++ 策略模式的应用场景主要包括以下几种情况:
(1)算法切换: 当系统需要在不同的算法之间进行切换以适应不同的性能需求时,可以使用策略模式。例如,排序算法在不同的场景中可能需要不同的实现,策略模式可以方便地在运行时选择不同的排序算法。
(2)多种行为处理: 如果一个对象拥有多种行为,而且这些行为在实现上各不相同,那么使用策略模式可以将这些行为分离到各自的策略类中,从而避免在对象内部使用多重条件语句来判断执行哪种行为。这种分离可以提高代码的可读性和可维护性。
(3)客户端与算法解耦: 当不希望客户端知道复杂的、与算法相关的数据结构时,策略模式可以将算法和相关的数据结构封装在具体的策略类中,从而提高算法的保密性和安全性。客户端只需要与策略接口交互,而不需要关心具体的实现细节。
(4)动态改变行为: 当需要在运行时动态地改变对象的行为时,策略模式非常有用。通过更改上下文对象所持有的策略对象,可以轻松地改变其行为。
3.1 策略模式应用于算法切换
以下是一个策略模式应用于算法切换的示例。在这个例子中,创建一个计算器的上下文类,它可以根据需要切换不同的计算策略(比如加法策略和乘法策略)。
#include <iostream>
#include <memory> // For std::unique_ptr // 策略接口
class CalculationStrategy {
public:virtual ~CalculationStrategy() = default;virtual int calculate(int a, int b) = 0;
};// 加法策略
class AdditionStrategy : public CalculationStrategy {
public:int calculate(int a, int b) override {return a + b;}
};// 乘法策略
class MultiplicationStrategy : public CalculationStrategy {
public:int calculate(int a, int b) override {return a * b;}
};// 上下文类
class Calculator {
public:// 设置策略 void setStrategy(std::unique_ptr<CalculationStrategy> newStrategy) {strategy = std::move(newStrategy);}// 执行计算 int executeCalculation(int a, int b) {if (!strategy) {throw std::runtime_error("No calculation strategy is set.");}return strategy->calculate(a, b);}private:std::unique_ptr<CalculationStrategy> strategy;
};int main()
{Calculator calculator;// 设置加法策略 calculator.setStrategy(std::make_unique<AdditionStrategy>());std::cout << "5 + 3 = " << calculator.executeCalculation(5, 3) << std::endl; // 输出:5 + 3 = 8 // 切换为乘法策略 calculator.setStrategy(std::make_unique<MultiplicationStrategy>());std::cout << "5 * 3 = " << calculator.executeCalculation(5, 3) << std::endl; // 输出:5 * 3 = 15 return 0;
}
上面这些代码的输出为:
5 + 3 = 8
5 * 3 = 15
在这个示例中,CalculationStrategy 是策略接口,定义了计算的方法。AdditionStrategy 和 MultiplicationStrategy 是具体的策略类,分别实现了加法和乘法。Calculator 是上下文类,它持有一个指向 CalculationStrategy 的智能指针,用于在运行时切换计算策略。客户端代码通过调用 setStrategy 方法来更改策略,并通过 executeCalculation 方法来执行计算。
通过这种方式,可以轻松地扩展新的计算策略,只需要实现 CalculationStrategy 接口,并在需要时将其设置为 Calculator 的策略即可。这种灵活性使得算法切换变得非常简单和直观。
3.2 策略模式应用于多种行为处理
当需要处理多种不同行为时,策略模式可以通过定义一组策略类并将它们封装在接口中,使得客户端代码能够根据需要选择并执行相应的行为。以下是一个策略模式应用于多种行为处理的示例:
#include <iostream>
#include <memory>
#include <string> // 策略接口
class BehaviorStrategy {
public:virtual ~BehaviorStrategy() = default;virtual void execute() = 0;
};// 飞行行为
class FlyBehavior : public BehaviorStrategy {
public:void execute() override {std::cout << "Flying high in the sky!" << std::endl;}
};// 游泳行为
class SwimBehavior : public BehaviorStrategy {
public:void execute() override {std::cout << "Swimming in the deep blue sea!" << std::endl;}
};// 跑步行为
class RunBehavior : public BehaviorStrategy {
public:void execute() override {std::cout << "Running fast on the ground!" << std::endl;}
};// 上下文类
class Context {
public:// 设置行为 void setBehavior(std::unique_ptr<BehaviorStrategy> newBehavior) {behavior = std::move(newBehavior);}// 执行当前行为 void performAction() {if (behavior) {behavior->execute();}else {std::cout << "No behavior is set." << std::endl;}}private:std::unique_ptr<BehaviorStrategy> behavior;
};int main()
{Context context;// 设置飞行行为 context.setBehavior(std::make_unique<FlyBehavior>());context.performAction(); // 输出:Flying high in the sky! // 切换为游泳行为 context.setBehavior(std::make_unique<SwimBehavior>());context.performAction(); // 输出:Swimming in the deep blue sea! // 切换为跑步行为 context.setBehavior(std::make_unique<RunBehavior>());context.performAction(); // 输出:Running fast on the ground! return 0;
}
上面代码的输出为:
Flying high in the sky!
Swimming in the deep blue sea!
Running fast on the ground!
在这个示例中,BehaviorStrategy 是策略接口,定义了执行行为的方法。FlyBehavior、SwimBehavior 和 RunBehavior 是具体的策略类,分别实现了不同的行为。Context 是上下文类,它持有一个指向 BehaviorStrategy 的智能指针,用于在运行时切换行为。客户端代码通过调用 setBehavior 方法来更改行为,并通过 performAction 方法来执行当前设置的行为。
这个示例展示了策略模式在多种行为处理中的应用,它使得行为的改变与客户端代码解耦,从而提高了代码的可维护性和灵活性。
4 策略模式的优点与缺点
C++ 策略模式的优点主要包括:
(1)灵活性: 策略模式允许在运行时动态地改变对象的行为。客户端代码只需要与策略接口交互,而不需要关心具体的实现细节。这使得在不需要修改现有客户端代码的情况下,可以轻松地为系统添加新的行为或算法。
(2)开闭原则: 策略模式遵循开闭原则,即对扩展开放,对修改封闭。通过增加新的策略类来实现新的行为,而不是修改现有的类,这样可以保持代码的稳定性和可维护性。
(3)代码复用: 由于策略模式将算法或行为封装在独立的策略类中,这些策略类可以被多个上下文对象复用,提高了代码的复用性。
(4)简化单元测试: 每个策略类都是独立的,可以单独进行单元测试,这有助于确保算法或行为的正确性,并简化测试过程。
然而,C++ 策略模式也存在一些缺点:
(1)客户端必须了解不同策略: 客户端代码需要知道有哪些策略可供选择,并需要显式地设置所需的策略。这增加了客户端代码的复杂性,并可能导致策略选择的错误。
(2)策略类数量可能增加: 随着系统中策略数量的增加,可能需要创建大量的策略类。这可能导致代码库变得庞大和难以管理。
(3)性能开销: 由于策略模式涉及到虚函数调用和可能的动态内存分配(如果使用智能指针管理策略对象),因此在性能敏感的场合可能会引入一定的开销。然而,这种开销通常与策略模式的灵活性相比是可以接受的。
(4)额外的接口设计: 设计良好的策略接口需要一定的经验和技巧。如果接口设计不当,可能会导致策略类之间的耦合度过高,违背了策略模式的初衷。
综上所述,C++策略模式在提供灵活性和可维护性的同时,也可能带来一些额外的复杂性和性能开销。因此,在决定是否使用策略模式时,需要根据具体的应用场景和需求进行权衡。
相关文章:
突破编程_C++_设计模式(策略模式)
1 策略模式的概念 策略模式(Strategy Pattern)是 C 中常用的一种行为设计模式,它能在运行时改变对象的行为。在策略模式中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为模式。 在策略模式中,需…...
【uniapp】uniapp小程序中实现拍照同时打开闪光灯的功能,拍照闪光灯实现
一、需求前提 特殊场景中,需要拍照的同时打开闪光灯,(例如黑暗场景下的设备维护巡检功能)。 起初我是用的uviewui中的u-upload组件自带的拍照功能,但是这个不支持拍照时打开闪光灯,也不支持从通知栏中打开…...
在python model train里如何驯服野生log?
关键词:python 、epoch、loss、log 🤖: 记录模型的训练过程的步骤如下: 导入logging模块。配置日志记录器,设置日志文件名、日志级别、日志格式等。在每个epoch结束时,使用logging模块记录性能指标、损失值、准确率等信…...
产品推荐 - Xilinx FPGA下载器 XQ-HS/STM2
1 FPGA下载器简介 1.性能优良 FPGA下载器XQ-HS/STM2采用Xilinx下载模块设计而成(JTAG-SMT2NC模块,该模块与Xilinx官方开发板KC705,KCU105,ZC702,ZC706,Zedboard等板载下载器一样,下载速度快…...
STM32 SDRAM知识点
1.SDRAM和SRAM的区别 SRAM不需要刷新电路即能保存它内部存储的数据。而SDRAM(Dynamic Random Access Memory)每隔一段时间,要刷新充电一次,否则内部的数据即会消失,因此SRAM具有较高的性能,但是SRAM也有它…...
手写分布式配置中心(六)整合springboot(自动刷新)
对于springboot配置自动刷新,原理也很简单,就是在启动过程中用一个BeanPostProcessor去收集需要自动刷新的字段,然后在springboot启动后开启轮询任务即可。 不过需要对之前的代码再次做修改,因为springboot的配置注入value("…...
记录一次排查负载均衡不能创建的排查过程
故障现象,某云上,运维同事在创建负载均衡的时候,发现可以创建资源,但是创建完之后,不显示对应的负载均衡。 创建负载均衡时候,按f12发现console有如下报错 后来请后端网络同事排查日志发现,是后…...
数据推送解决方案调研
需求 文档编辑类型的需求,左侧是菜单栏,右侧是内容块,现在的需求时,如果多人同时编辑这个方案,当添加章节/调整章节顺序/删除章节时,其他用户能够及时感知到。 解决方案调研 前端轮询 最简单的方案&…...
二、NLP中的序列标注(分词、主体识别)
一般来说,一个序列指的是一个句子,而一个元素指的是句子中的一个词。在序列标注中,我们想对一个序列的每一个元素标注一个分类标签。比如信息提取问题可以认为是一个序列标注问题,如提取出会议时间、地点等。 常见的应用场景&…...
seq2seq翻译实战-Pytorch复现
🍨 本文为[🔗365天深度学习训练营学习记录博客 🍦 参考文章:365天深度学习训练营 🍖 原作者:[K同学啊 | 接辅导、项目定制]\n🚀 文章来源:[K同学的学习圈子](https://www.yuque.com/…...
软考69-上午题-【面向对象技术2-UML】-关系
一、关系 UML中有4种关系: 依赖;关联;泛化;实现。 1-1、依赖 行为(参数),参数就是被依赖的事物,即:独立事物。 当独立事物发生变化时,依赖事务行为的语义也…...
智慧文旅|AI数字人导览:让旅游体验不再局限于传统
AI数字人导览作为一种创新的展示方式,已经逐渐成为了VR全景领域的一大亮点,不仅可以很好的嵌入在VR全景中,更是能够随时随地为观众提供一种声情并茂的讲解介绍,结合VR场景的沉浸式体验,让观众仿佛置身于真实场景之中&a…...
spring boot 集成 mysql ,mybatisplus多数据源
1、需要的依赖,版本自行控制 <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId> </dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java<…...
CLion中常用快捷键(仍适用其他编译软件)
基本编辑操作: 复制:Ctrl C粘贴:Ctrl V剪切:Ctrl X撤销:Ctrl Z重做:Ctrl Shift Z (不小心撤销了 需要返回之前的操作 相当于下一步)全选:Ctrl A 导航࿱…...
考研复习c语言初阶(1)
本人准备考研,现在开始每天更新408的内容,目标这个月结束C语言和数据结构,每天更新~ 一.再次认识c语言 C语言是一门通用计算机编程语言,广泛应用于底层开发。C语言的设计目标是提供一种能以简易 的方式编译、处理低级存储器、产生…...
HTML—常用标签
常用标签: 标题标签:<h1></h1>......<h6></h6>段落标签:<p></p>换行标签:<br/>列表:无序列表<ul><li></li></ul> 有序列表<ol>&…...
Midjourney绘图欣赏系列(七)
Midjourney介绍 Midjourney 是生成式人工智能的一个很好的例子,它根据文本提示创建图像。它与 Dall-E 和 Stable Diffusion 一起成为最流行的 AI 艺术创作工具之一。与竞争对手不同,Midjourney 是自筹资金且闭源的,因此确切了解其幕后内容尚不…...
深度学习应该如何入门?
深度学习是一门令人着迷的领域,但初学者可能会感到有些困惑。让我们从头开始,用通俗易懂的语言来探讨深度学习的基础知识。 1. 基础知识 深度学习需要一些数学和编程基础。首先,我们要掌握一些数学知识,如线性代数、微积分和概率…...
FreeRtos Queue(五)
本篇主要分析在中断中向队列里发消息xQueueGenericSendFromISR和在中断里从队列中读取消息xQueueReceiveFromISR。 前言: xQueueGenericSendFromISR 和 xQueueReceiveFromISR都是在中断里调用的而不是任务里调用的,所以队列满了或者是队列为空的时候自然就没有把当…...
解决虚拟机静态网址设置后还是变动的的问题
源头就是我的虚拟机静态网址设置好了以后但是网址还是会变动 这是我虚拟机的配置 vi /etc/sysconfig/network-scripts/ifcfg-ens33 这是出现的问题 进入这里 cd /etc/sysconfig/network-scripts/ 然后我去把多余的ens33的文件都删了 然后还不行 后来按照这个图片进行了下 然后…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
