C++设计模式——Strategy策略模式

2024-09-04 09:44

本文主要是介绍C++设计模式——Strategy策略模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一,策略模式简介

策略模式是一种行为型设计模式,策略模式在软件开发场景中定义了一系列的算法,并将每个算法单独封装在可替换的对象中,使应用程序在运行时可以根据具体的上下文来动态地选择和切换算法,同时保持原有的代码架构不被修改。

策略模式的设计使得算法的实现与调用被分离,让算法可以独立于外部客户端进行开发和改动,使用独立的类来封装特定的算法,也避免了不同算法策略之间的互相影响。

策略模式能适应多种应用场景,为了满足业务需求,应用程序在运行时可以选择不同的算法策略来达到最优的实现效果。

策略模式将不同的算法实现封装成独立的类,使得算法的修改不会影响到客户端代码,提高了应用程序的灵活性和可维护性。策略模式的架构可以避免使用大量的if-else条件语句来大量判断不同的策略分支,优化了代码结构,增加了代码的可扩展性。

策略模式在现实生活中的抽象实例:

交通路线选择:当我们在规划行程路线时,根据行驶时间、行驶距离,可以规划出好几个不同的路线策略。

投资策略选择:投资者在选择投资策略时,可能会考虑不同的策略,比如价值、成长、指数等。

健身计划选择:在健身时,个人根据不同的健身进展选择不同的策略,如有氧运动、力量训练、高强度间歇训练等。

二,策略模式的结构

策略模式主要包含以下组件:

1.策略上下文(Context):

Context类是策略模式的调度核心,其内部包含了一个策略对象,并通过调用具体的策略对象来完成具体操作。Context类对外提供了与客户端交互的API接口,并隐藏了具体的算法细节,Context类相当于一个中间件,将算法封装与客户端调用进行了分离。

2.抽象策略类(Strategy):

Strategy类定义了一个公共接口,该公共接口最终将被具体的算法模块进行实现和重写。

3.具体策略类(ConcreteStrategy):

ConcreteStrategy类实现了Strategy类定义的公共接口,每一个具体策略类都包含特定的算法实现细节,并用来处理特定的应用场景。

组件之间的工作步骤如下:

1.客户端根据业务需要选择一个具体策略类,并初始化一个对应的策略对象。

2.客户端将创建好的策略对象传递给策略上下文。

3.策略上下文调用策略对象的接口函数。

4.当客户端需要更换算法策略时,可以重新选择一个具体策略类,并传递一个新的策略对象给策略上下文。

对应UML类图:

三,策略模式代码样例

Demo1:根据不同的策略,计算出不同的结果

#include <iostream>class Strategy {
public:virtual int operation(int input) const = 0;
};class ConcreteStrategyA : public Strategy {
public:int operation(int input) const override {return input * 2;}
};class ConcreteStrategyB : public Strategy {
public:int operation(int input) const override {return input / 2;}
};class Context {
private:Strategy* strategy;
public:Context(Strategy* strategy = nullptr) {this->strategy = strategy;}void setStrategy(Strategy* strategy) {this->strategy = strategy;}int execute(int input) const {return strategy->operation(input);}
};int main() {Context context(new ConcreteStrategyA());std::cout << "Using Strategy A: " << context.execute(10) << std::endl;context.setStrategy(new ConcreteStrategyB()); std::cout << "Using Strategy B: " << context.execute(10) << std::endl;return 0;
}

运行结果:

Using Strategy A: 20
Using Strategy B: 5

Demo2:根据不同的策略,打印不同的提示语

#include <iostream>class Strategy {
public:virtual void execute() const = 0;
};class ConcreteStrategyA : public Strategy {
public:virtual void execute() const override {std::cout << "Using Strategy A." << std::endl;}
};class ConcreteStrategyB : public Strategy {
public:virtual void execute() const override {std::cout << "Using Strategy B." << std::endl;}
};class Context {
private:Strategy* strategy;
public:Context(Strategy* strategy) : strategy(strategy) {}void setStrategy(Strategy* strategy) {this->strategy = strategy;}void executeStrategy() const {strategy->execute();}
};int main() {Strategy* strategyA = new ConcreteStrategyA();Strategy* strategyB = new ConcreteStrategyB();Context context(strategyA);context.executeStrategy();context.setStrategy(strategyB);context.executeStrategy();delete strategyA;delete strategyB;return 0;
}

运行结果:

Using Strategy A.
Using Strategy B.

四,策略模式的应用场景

文件格式处理:代码可以根据不同的文件格式选择不同的解析策略,例如文本格式、XML、JSON等。

交易系统开发:系统根据交易类型、支付渠道等,选择不同的处理策略。

可配置应用开发:用户可以根据不同的业务场景来动态选择不同的配置模板。

通用API开发:当同一个API需要提供多个版本或业务逻辑时,策略模式可以帮助隐藏具体细节。

五,策略模式的优缺点

策略模式的优点:

对“开闭原则”提供完美支持。

基于上下文和算法类的封装,方便管理和调度一系列算法策略。

避免了if-else条件语句的大量使用。

支持灵活的替换算法策略,代码的可读性和扩展性很强。

算法被封装以后,可以独立地被多个客户端和上下文复用。

策略模式的缺点:

使类和对象的数量变得更多,增加了系统的复杂性。

如果策略被划分得过于细化,会导致过度设计,不易于代码理解。

代码涉及多个对象的创建和销毁,性能开销增大,大量使用会引起性能问题。

六,代码实战

Demo:集成冒泡排序算法、选择排序算法给客户端进行调用

#include <iostream>
#include <vector>class SortingStrategy {
public:virtual void sort(std::vector<int>& arr) = 0;
};class BubbleSort: public SortingStrategy {
public:void sort(std::vector<int>& arr) override{std::cout << "\nUse Strategy: BubbleSort." << std::endl;int n = arr.size();for (size_t i = 0; i < n - 1; ++i) {for (size_t j = 0; j < n - i - 1; ++j) {if (arr[j] > arr[j + 1]) {std::swap(arr[j], arr[j + 1]);}}}}
};class SelectionSort: public SortingStrategy {
public:void sort(std::vector<int>& arr) override{std::cout << "\nUse Strategy: SelectionSort." << std::endl;int n = arr.size();for (int i = 0; i < n - 1; ++i) {int minIndex = i;for (int j = i + 1; j < n; ++j) {if (arr[j] < arr[minIndex]) {minIndex = j;}}std::swap(arr[i], arr[minIndex]);}}
};class SortContext {
private:SortingStrategy* strategy;public:void setStrategy(SortingStrategy* strategy){this->strategy = strategy;}void executeStrategy(std::vector<int>& arr){strategy->sort(arr);}
};int main()
{std::vector<int> data = { 23, 5, 6, 36, 25, 4, 20 };SortContext context;BubbleSort bubbleSort;SelectionSort selectSort;context.setStrategy(&bubbleSort);context.executeStrategy(data);for (const auto& num : data) {std::cout << num << " ";}data = { 32, 45, 5, 6, 100, 7 };context.setStrategy(&selectSort);context.executeStrategy(data);for (const auto& num : data) {std::cout << num << " ";}return 0;
}

运行结果:

Use Strategy: BubbleSort.
4 5 6 20 23 25 36
Use Strategy: SelectionSort.
5 6 7 32 45 100

七,参考阅读

https://www.geeksforgeeks.org/strategy-method-design-pattern-c-design-patterns/

https://refactoringguru.cn/design-patterns/strategy

https://design-patterns.readthedocs.io/zh-cn/latest/behavioral_patterns/strategy.html

https://www.vishalchovatiya.com/strategy-design-pattern-in-modern-cpp/

这篇关于C++设计模式——Strategy策略模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1135668

相关文章

SpringBoot基于配置实现短信服务策略的动态切换

《SpringBoot基于配置实现短信服务策略的动态切换》这篇文章主要为大家详细介绍了SpringBoot在接入多个短信服务商(如阿里云、腾讯云、华为云)后,如何根据配置或环境切换使用不同的服务商,需... 目录目标功能示例配置(application.yml)配置类绑定短信发送策略接口示例:阿里云 & 腾

Nginx location匹配模式与规则详解

《Nginxlocation匹配模式与规则详解》:本文主要介绍Nginxlocation匹配模式与规则,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、环境二、匹配模式1. 精准模式2. 前缀模式(不继续匹配正则)3. 前缀模式(继续匹配正则)4. 正则模式(大

redis过期key的删除策略介绍

《redis过期key的删除策略介绍》:本文主要介绍redis过期key的删除策略,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录第一种策略:被动删除第二种策略:定期删除第三种策略:强制删除关于big key的清理UNLINK命令FLUSHALL/FLUSHDB命

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

SpringRetry重试机制之@Retryable注解与重试策略详解

《SpringRetry重试机制之@Retryable注解与重试策略详解》本文将详细介绍SpringRetry的重试机制,特别是@Retryable注解的使用及各种重试策略的配置,帮助开发者构建更加健... 目录引言一、SpringRetry基础知识二、启用SpringRetry三、@Retryable注解

MySQL 分区与分库分表策略应用小结

《MySQL分区与分库分表策略应用小结》在大数据量、复杂查询和高并发的应用场景下,单一数据库往往难以满足性能和扩展性的要求,本文将详细介绍这两种策略的基本概念、实现方法及优缺点,并通过实际案例展示如... 目录mysql 分区与分库分表策略1. 数据库水平拆分的背景2. MySQL 分区策略2.1 分区概念

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve