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

相关文章

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

C++11右值引用与Lambda表达式的使用

《C++11右值引用与Lambda表达式的使用》C++11引入右值引用,实现移动语义提升性能,支持资源转移与完美转发;同时引入Lambda表达式,简化匿名函数定义,通过捕获列表和参数列表灵活处理变量... 目录C++11新特性右值引用和移动语义左值 / 右值常见的左值和右值移动语义移动构造函数移动复制运算符

C++中detach的作用、使用场景及注意事项

《C++中detach的作用、使用场景及注意事项》关于C++中的detach,它主要涉及多线程编程中的线程管理,理解detach的作用、使用场景以及注意事项,对于写出高效、安全的多线程程序至关重要,下... 目录一、什么是join()?它的作用是什么?类比一下:二、join()的作用总结三、join()怎么

C++中全局变量和局部变量的区别

《C++中全局变量和局部变量的区别》本文主要介绍了C++中全局变量和局部变量的区别,全局变量和局部变量在作用域和生命周期上有显著的区别,下面就来介绍一下,感兴趣的可以了解一下... 目录一、全局变量定义生命周期存储位置代码示例输出二、局部变量定义生命周期存储位置代码示例输出三、全局变量和局部变量的区别作用域

C++中assign函数的使用

《C++中assign函数的使用》在C++标准模板库中,std::list等容器都提供了assign成员函数,它比操作符更灵活,支持多种初始化方式,下面就来介绍一下assign的用法,具有一定的参考价... 目录​1.assign的基本功能​​语法​2. 具体用法示例​​​(1) 填充n个相同值​​(2)

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、统一