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

相关文章

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

C++中NULL与nullptr的区别小结

《C++中NULL与nullptr的区别小结》本文介绍了C++编程中NULL与nullptr的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编... 目录C++98空值——NULLC++11空值——nullptr区别对比示例 C++98空值——NUL

C++ Log4cpp跨平台日志库的使用小结

《C++Log4cpp跨平台日志库的使用小结》Log4cpp是c++类库,本文详细介绍了C++日志库log4cpp的使用方法,及设置日志输出格式和优先级,具有一定的参考价值,感兴趣的可以了解一下... 目录一、介绍1. log4cpp的日志方式2.设置日志输出的格式3. 设置日志的输出优先级二、Window

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

c++中的set容器介绍及操作大全

《c++中的set容器介绍及操作大全》:本文主要介绍c++中的set容器介绍及操作大全,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录​​一、核心特性​​️ ​​二、基本操作​​​​1. 初始化与赋值​​​​2. 增删查操作​​​​3. 遍历方

解析C++11 static_assert及与Boost库的关联从入门到精通

《解析C++11static_assert及与Boost库的关联从入门到精通》static_assert是C++中强大的编译时验证工具,它能够在编译阶段拦截不符合预期的类型或值,增强代码的健壮性,通... 目录一、背景知识:传统断言方法的局限性1.1 assert宏1.2 #error指令1.3 第三方解决