突破编程_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++策略模式在提供灵活性和可维护性的同时,也可能带来一些额外的复杂性和性能开销。因此,在决定是否使用策略模式时,需要根据具体的应用场景和需求进行权衡。

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

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

相关文章

BUUCTF----[极客大挑战 2019]HardSQL

输入1’ 单引号闭合 进行永真式判断 竟然说我是臭弟弟----八嘎&#xff08;肯定是进行了过滤&#xff09; 经过手法判断&#xff0c;过滤了&#xff0c;空格&#xff0c;and等报错注入updatexml() 报错注入顾名思义就是&#xff0c;通过特殊函数错误使用并使其输出错误结果来获…

性能测试能力提升 —— 线程、并发、吞吐量、TPS、QPS、响应时间

一、背景 接着上一篇的知识&#xff1a;性能测试能力提升-关于性能测试&#xff0c;本篇文章&#xff0c;我们将主要介绍以下几方面的知识&#xff1a; 线程数&并发用户数相对并发&绝对并发吞吐量TPS&QPS响应时间 二、线程数、并发用户数 线程数: 主流的性能测…

Android Studio轮播图使用失败怎么办【已解决】

Android Studio轮播图使用失败怎么办 1.在gethub上面搜索轮播图 2.选择要使用的轮播图 3.查看该轮播图的配置方法 4.复制该依赖放入build.gradle中 5.重新构建 6.使用banner 发现没有报错了 7.参考网址 https://github.com/youth5201314/banner

解读BOT攻击,探索灵活高效的防护之道

回顾早期的互联网应用&#xff0c;由于业务流量比较小&#xff0c;往往单台服务器就能满足负载需求。随着互联网的流量越来越大&#xff0c;单服务器已经不能满足业务需求&#xff0c;无论它优化得再好&#xff0c;都较难承受大量的访问压力。支持负载均衡的技术很多&#xff0…

微信加好友频繁会被封号吗?

微信加好友频繁会被封号吗&#xff1f; 微信规定,每个人每天最多可以加20个好友&#xff0c;但一天之内如果频繁加好友&#xff0c;微信可能会出现异常提示&#xff0c;需要暂停好友添加操作。 面对微信上突如其来的大量好友申请&#xff0c;一定要谨慎处理&#xff0c;以免被…

新增流计算计数窗口,TDengine 3.2.3.0 八大板块功能更新

自发布以来&#xff0c;TDengine 3.0 版本在研发人员和社区用户的共同努力下不断优化&#xff0c;产品的稳定性和易用性获得了大幅提升&#xff0c;在知轮科技的智慧轮胎系统、黑格智能 3D 打印业务、韵达快递业务、中国地震台网中心、中移物联智慧出行场景等众多企业项目中获得…

前端面试练习24.3.5

webpack相关 项目使用webpack流程 进入一个初始化好的vue项目下载安装webpack相关依赖包/插件 npm install --save-dev webpack webpack-cli webpack-dev-server安装一些相关的loader,比如vue-loader&#xff0c;babel-loader&#xff0c;css-loader等创建webpack.config.js文…

【开源】SpringBoot框架开发教学资源共享平台

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 课程档案模块2.3 课程资源模块2.4 课程作业模块2.5 课程评价模块 三、系统设计3.1 用例设计3.2 类图设计3.3 数据库设计3.3.1 课程档案表3.3.2 课程资源表3.3.3 课程作业表3.3.4 课程评价表 四、系统展…

【基于springboot+Vue+Element ui的电影推荐之协同过滤算法简单实现】

基于springbootVueElement ui的电影推荐之协同过滤算法简单实现 1.基于用户的协同过滤算法的简单设计与实现1.1获取某个用户的评分矩阵1.2获取该用户与其他用户的相似度矩阵1.3获取两个用户之间的相似度并存储1.4返回推荐列表 2.基于物品的协同过滤算法的简单设计与实现2.1计算…

蓝桥杯每日一题(哈希、单调队列)

2058 笨拙的手指 二进制所有的可能保存进哈希表&#xff0c;三进制找出所有的可能判断哈希表中是否有数字。 注意一种情况就是修改完之后出现前导零。直接continue; 学到了&#xff0c;某些条件的限制不一定要在循环条件上&#xff0c;可以直接在循环体内。 #include<bi…

运维:记一次寻找定时任务并删除的经历

前言 我相信接手别人的服务器、或者在没有任何文档的情况去看自己原先的服务器,都或多或少会遇到莫名其妙的服务器独有规则。 比如你服务本身跑的好好的,突然啪的一下,没了! 什么原因导致的呢?其中,很大可能是定时任务在作祟。 原因分析 本次,我遇到的问题是:在Ubuntu系…

一分钟了解遥感中卫星、传感器、波段及数据之间的关系

感是利用卫星、飞机或其他载具上的传感器对地球表面进行观测和测量的科学技术。以下是一些常见的遥感相关术语: 卫星(Satellite):在遥感中,卫星是指绕地球轨道运行的人造卫星,其主要任务是携带各种传感器从空间中对地球表面进行观测。 传感器(Sensor):传感器是安装在卫…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:ImageSpan)

Text组件的子组件&#xff0c;用于显示行内图片。 说明&#xff1a; 该组件从API Version 10开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 ImageSpan(value: ResourceStr | PixelMap) 参数&#xff1a; 参数名参数类…

抓包工具获取请求信息

Charles 下载安装 下载 官方下载地址&#xff1a;https://www.charlesproxy.com/latest-release/download.do 下载后傻瓜式安装就好&#xff0c;这个官方的需要激活&#xff0c;可以选择绿色版或者学习版 绿色版 绿色中文版&#xff1a;https://soft.kxdw.com/pc/Charles.z…

【代码】Android|获取存储权限并创建、存储文件

版本&#xff1a;Android 11及以上&#xff0c;gradle 7.0以上&#xff0c;Android SDK > 29 获取存储权限 获取存储权限参考&#xff1a;Android 11 外部存储权限适配指南及方案&#xff0c;这篇文章直接翻到最下面&#xff0c;用XXPermissions框架。它漏了这个框架的使用方…

opencv实现视频人脸识别

一. 实现指定图像的人脸识别 注意&#xff1a; 以下实例参考《OpenCV轻松入门面向Python》李立宗著&#xff0c;使用python语言&#xff0c;编辑器为PyCharm&#xff0c;且都运行成功。 1.dface3.jpg图片文件和当前代码放在同一级目录下。 2.级联分类器文件和当前代码文件放在…

MapReduce-Partition分区

Partition分区 1.默认Partitioner分区 (key.hashcode() & Interger.MAX_VALUE) % numReduceTasksnumReduceTasks默认为&#xff1a;1 //输出文件一个默认分区根据key的hashCode对ReduceTasks个数取模。 用户控制那个key存储到那个分区2.手动设置分区 //设置分区 job.set…

笔记78:软件包管理工具 apt 详解(包含常用 apt 命令介绍)

一、Ubuntu 的包管理工具 apt 过去&#xff0c;软件通常是从源代码安装的&#xff0c;安装步骤为&#xff1a;​​​​​​ 在Github上下载该软件的源码文件&#xff1b;查看Github上这个软件项目中提供的自述文件&#xff08;通常包含配置脚本或 makefile 文件&#xff09;&a…

软件架构的风格

1.数据流风格 数据流风格中&#xff0c;所有的数据按照流的形式在执行过程中前进&#xff0c;不存在结构的反复和重构&#xff0c;就像工厂中的汽车流水线一样&#xff0c;数据在流水线的各个节点上被加工。每完成一个环节&#xff0c;数据流都会被送入下一个环节&#xff0c;最…

Harbor二次开发前端环境搭建

1 前端开发环境搭建 &#xff08;1&#xff09;拉取分支代码 &#xff08;2&#xff09;前端开发推荐使用VsCode编辑器打开项目 打开 harbor\src\portal 文件夹&#xff0c;该文件夹为Harbor对应的前端代码所在位置 &#xff08;3&#xff09;在portal文件夹下创建名为 pro…