突破编程_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的文件都删了 然后还不行 后来按照这个图片进行了下 然后…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...