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

策略模式

    • 策略模式
      • 主要组成部分
      • 例一:逐步重构并引入策略模式
        • 第一步:初始实现
        • 第二步:提取共性并实现策略接口
        • 第三步:实现具体策略类
        • 第四步:实现上下文类
        • 策略模式 UML 图
          • 策略模式的 UML 图解析
      • 例二:逐步重构并引入策略模式
        • 第一步:初始实现
        • 第二步:提取共性并实现策略接口
        • 第三步:实现具体策略类
        • 第四步:实现上下文类:
        • 第五步:更新主函数
      • 策略模式 UML 图
        • 策略模式的 UML 图解析
      • 策略模式优缺点
      • 策略模式适用场景
      • 例一的完整代码
      • 例二的完整代码

策略模式

策略模式是一种行为设计模式,它定义了一系列算法,将每一个算法封装起来,并使它们可以互换。策略模式让算法的变化独立于使用算法的客户。

引入"策略“设计模式的定义:定义一些列算法类(策略类),将每个算法封装起来,让他们可以互相替换。换句话说,策略模式通常把一系列算法封装到一系列具体策略类中作为抽象策略类的字类,然后根据实际需要适用这些字类。

主要组成部分

  • 策略接口 (Strategy):定义一个统一的接口,声明所有支持的算法(策略)的方法。客户端通过这个接口调用具体策略。

  • 具体策略类 (Concrete Strategy):实现策略接口,定义具体的算法或行为。每个具体策略类实现不同的算法。

  • 上下文类 (Context):持有对策略接口的引用,负责调用具体策略的方法。上下文类可以在运行时动态地切换策略。

例一:逐步重构并引入策略模式

假设我们需要实现不同的排序算法(如冒泡排序和快速排序)。最初的实现可能是重复的代码。逐步重构的过程如下:

第一步:初始实现

为每种排序算法编写独立的处理逻辑:

#include <iostream>
#include <vector>void bubbleSort(std::vector<int>& arr) {for (size_t i = 0; i < arr.size() - 1; ++i) {for (size_t j = 0; j < arr.size() - i - 1; ++j) {if (arr[j] > arr[j + 1]) {std::swap(arr[j], arr[j + 1]);}}}
}void quickSort(std::vector<int>& arr, int low, int high) {if (low < high) {int pivot = arr[high];int i = low - 1;for (int j = low; j < high; ++j) {if (arr[j] < pivot) {++i;std::swap(arr[i], arr[j]);}}std::swap(arr[i + 1], arr[high]);quickSort(arr, low, i);quickSort(arr, i + 2, high);}
}int main() {std::vector<int> data = {5, 3, 8, 6, 2};bubbleSort(data);// 或者使用 quickSort(data, 0, data.size() - 1);return 0;
}
第二步:提取共性并实现策略接口

识别出排序的共性步骤,并创建一个策略接口 SortStrategy,定义排序方法:

class SortStrategy {
public:virtual void sort(std::vector<int>& arr) = 0; // 策略接口
};
第三步:实现具体策略类

为每种排序算法实现具体策略类,重写排序方法:

class BubbleSort : public SortStrategy {
public:void sort(std::vector<int>& arr) override {for (size_t i = 0; i < arr.size() - 1; ++i) {for (size_t j = 0; j < arr.size() - i - 1; ++j) {if (arr[j] > arr[j + 1]) {std::swap(arr[j], arr[j + 1]);}}}}
};class QuickSort : public SortStrategy {
public:void sort(std::vector<int>& arr) override {quickSort(arr, 0, arr.size() - 1);}private:void quickSort(std::vector<int>& arr, int low, int high) {if (low < high) {int pivot = arr[high];int i = low - 1;for (int j = low; j < high; ++j) {if (arr[j] < pivot) {++i;std::swap(arr[i], arr[j]);}}std::swap(arr[i + 1], arr[high]);quickSort(arr, low, i);quickSort(arr, i + 2, high);}}
};
第四步:实现上下文类

创建一个上下文类 Sorter,用于使用策略:

class Sorter {
private:SortStrategy* strategy;public:Sorter(SortStrategy* strategy) : strategy(strategy) {}void setStrategy(SortStrategy* strategy) {this->strategy = strategy;}void sort(std::vector<int>& arr) {strategy->sort(arr);}
};
策略模式 UML 图

策略模式 UML 图1

策略模式的 UML 图解析
  • 上下文类 (Context)

    • Sorter:它是上下文类,负责使用策略并持有策略的引用。Sorter 类通过 setStrategy 方法设置具体的排序策略,并通过 sort 方法调用该策略进行排序。
  • 抽象策略类 (Strategy)

    • SortStrategy:这是策略接口,定义了一个公共方法 sort,该方法由具体策略类实现。它确保所有具体策略类都有一致的接口。
  • 具体策略类 (Concrete Strategy)

    • BubbleSort:实现了 SortStrategy 接口,提供了冒泡排序的具体实现。
    • QuickSort:实现了 SortStrategy 接口,提供了快速排序的具体实现。

例二:逐步重构并引入策略模式

第一步:初始实现

首先,从一个简单的实现开始,其中战斗者类直接实现道具使用逻辑。

#include <iostream>
using namespace std;class Fighter {
public:Fighter(int life) : m_life(life) {}void UseBXD() { // 使用补血丹m_life += 200;cout << "使用补血丹,生命值增加200。" << endl;}void UseDHD() { // 使用大还丹m_life += 300;cout << "使用大还丹,生命值增加300。" << endl;}void UseSHD() { // 使用守护丹m_life += 500;cout << "使用守护丹,生命值增加500。" << endl;}int GetLife() {return m_life;}private:int m_life;
};int main() {Fighter warrior(1000);cout << "初始生命值:" << warrior.GetLife() << endl;warrior.UseDHD();cout << "当前生命值:" << warrior.GetLife() << endl;warrior.UseBXD();cout << "当前生命值:" << warrior.GetLife() << endl;warrior.UseSHD();cout << "当前生命值:" << warrior.GetLife() << endl;return 0;
}
第二步:提取共性并实现策略接口

在该实现中,Fighter 类包含所有道具使用的逻辑。可以提取出道具使用的共性,定义一个道具策略接口。

class ItemStrategy {
public:virtual void UseItem(Fighter* fighter) = 0;virtual ~ItemStrategy() {}
};
第三步:实现具体策略类

为每种道具创建具体的策略类,实现 ItemStrategy 接口。

  • 补血丹策略类
class ItemStrategy_BXD : public ItemStrategy {
public:void UseItem(Fighter* mainobj) override {mainobj->SetLife(mainobj->GetLife() + 200); // 补充200点生命值}
};
  • 大还丹策略类
class ItemStrategy_DHD : public ItemStrategy {
public:void UseItem(Fighter* mainobj) override {mainobj->SetLife(mainobj->GetLife() + 300); // 补充300点生命值}
};
  • 守护丹策略类
class ItemStrategy_SHD : public ItemStrategy {
public:void UseItem(Fighter* mainobj) override {mainobj->SetLife(mainobj->GetLife() + 500); // 补充500点生命值}
};
第四步:实现上下文类:

Fighter 类中添加设置道具策略的方法,并移除具体的道具使用逻辑,并定义战斗者的子类(可以不需要字类)。

class Fighter {
public:Fighter(int life) : m_life(life), itemStrategy(nullptr) {}void SetItemStrategy(ItemStrategy* strategy) {itemStrategy = strategy;}void UseItem() {if (itemStrategy) {itemStrategy->UseItem(this);}}int GetLife() {return m_life;}void SetLife(int life) {m_life = life;}private:int m_life;ItemStrategy* itemStrategy;
};
class F_Warrior : public Fighter {
public:F_Warrior(int life, int magic, int attack) : Fighter(life, magic, attack) {}// 其他战士特有的方法
};class F_Mage : public Fighter {
public:F_Mage(int life, int magic, int attack) : Fighter(life, magic, attack) {}// 其他法师特有的方法
};
第五步:更新主函数

更新主函数以使用新的策略模式结构。

int main() {Fighter warrior(1000);cout << "初始生命值:" << warrior.GetLife() << endl;ItemStrategy* strategy1 = new ItemStrategy_DHD();warrior.SetItemStrategy(strategy1);warrior.UseItem();cout << "当前生命值:" << warrior.GetLife() << endl;ItemStrategy* strategy2 = new ItemStrategy_BXD();warrior.SetItemStrategy(strategy2);warrior.UseItem();cout << "当前生命值:" << warrior.GetLife() << endl;ItemStrategy* strategy3 = new ItemStrategy_SHD();warrior.SetItemStrategy(strategy3);warrior.UseItem();cout << "当前生命值:" << warrior.GetLife() << endl;delete strategy1;delete strategy2;delete strategy3;return 0;
}

策略模式 UML 图

策略模式 UML 图2

策略模式的 UML 图解析
  • Context(环境类):也叫上下文类,是使用算法的角色,该类中维持着一个对抽象策略类的指针或引用。这里指Fighter类。
  • Stategy(抽象策略类):定义所支持的算法的公共接口,是所有策略类的父类。这里指 ItemStrategy 类。
  • ConcreteStrategy(具体策略类):抽象策略类的子类,实现抽象策略类中声明的接口。这里指ItemStrategy_BXDItemStrategy_DHDItemStrategy_SHD类。

策略模式优缺点

优点

  • 灵活性:可以在运行时选择不同的策略,增加了程序的灵活性和可扩展性。

  • 开放-关闭原则:新的策略可以通过实现策略接口而无需修改现有代码,符合开放-关闭原则。

  • 清晰的职责分离:将不同的算法封装在不同的类中,使得代码更加清晰,易于维护。

  • 减少条件语句:避免了使用大量的条件语句(如 if-elseswitch),使代码结构更加简洁。

  • 易于测试:每个策略类可以独立测试,便于单元测试和调试。

缺点

  • 类的数量增加:每种策略都需要一个新的类,可能导致类的数量增加,增加了系统的复杂性。

  • 客户端必须了解所有策略:客户端需要了解所有可用的策略,以便选择合适的策略,这可能增加了使用的复杂性。

  • 性能开销:在某些情况下,频繁地创建和销毁策略对象可能导致性能开销。

  • 不适合简单的算法:对于简单的算法,使用策略模式可能显得过于复杂,增加了不必要的抽象。

策略模式适用场景

  • 多种算法:当有多个算法可以选择时,策略模式可以将它们封装起来,方便切换。
  • 避免条件语句:当使用大量条件语句来选择算法时,可以使用策略模式来简化代码结构。
  • 动态选择算法:当需要在运行时选择算法时,策略模式提供了灵活性。
  • 算法复用:当多个类需要使用同一算法时,可以将算法封装成策略类,促进代码复用。

例一的完整代码

以下是完整的实现代码:

#include <iostream>
#include <vector>// 策略接口
class SortStrategy
{
public:virtual void sort(std::vector<int>& arr) = 0; // 策略接口
};// 具体策略:冒泡排序
class BubbleSort : public SortStrategy {
public:void sort(std::vector<int>& arr) override {for (size_t i = 0; i < arr.size() - 1; ++i) {for (size_t j = 0; j < arr.size() - i - 1; ++j) {if (arr[j] > arr[j + 1]) {std::swap(arr[j], arr[j + 1]);}}}}
};// 具体策略:快速排序
class QuickSort : public SortStrategy {
public:void sort(std::vector<int>& arr) override {quickSort(arr, 0, arr.size() - 1);}private:void quickSort(std::vector<int>& arr, int low, int high) {if (low < high) {int pivot = arr[high];int i = low - 1;for (int j = low; j < high; ++j) {if (arr[j] < pivot) {++i;std::swap(arr[i], arr[j]);}}std::swap(arr[i + 1], arr[high]);quickSort(arr, low, i);quickSort(arr, i + 2, high);}}
};// 上下文类
class Sorter {
private:SortStrategy* strategy;public:Sorter(SortStrategy* strategy) : strategy(strategy) {}void setStrategy(SortStrategy* strategy) {this->strategy = strategy;}void sort(std::vector<int>& arr) {strategy->sort(arr);}
};// 示例用法
int main() {std::vector<int> data = {5, 3, 8, 6, 2};Sorter sorter(new BubbleSort());sorter.sort(data); // 使用冒泡排序sorter.setStrategy(new QuickSort());sorter.sort(data); // 使用快速排序return 0;
}

例二的完整代码

#include <iostream>
using namespace std;class Fighter; // 类前向声明//道具策略类的父类
class ItemStrategy
{
public:virtual void UseItem(Fighter* mainobj) = 0;virtual ~ItemStrategy() {}
};//战斗者父类
class Fighter
{
public:Fighter(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack) {}virtual ~Fighter() {}public:void SetItemStrategy(ItemStrategy* strategy) //设置道具使用的策略{itemstrategy = strategy;}void UseItem() //使用道具(吃药){itemstrategy->UseItem(this);}int  GetLife() //获取人物生命值{return m_life;}void SetLife(int life) //设置人物生命值{m_life = life;}private:ItemStrategy* itemstrategy = nullptr; //C++11中支持这样初始化protected:int m_life;int m_magic;int m_attack;
};//“战士”类,父类为Fighter
class F_Warrior :public Fighter
{
public:F_Warrior(int life, int magic, int attack) :Fighter(life, magic, attack) {}
};//“法师”类,父类为Fighter
class F_Mage :public Fighter
{
public:F_Mage(int life, int magic, int attack) :Fighter(life, magic, attack) {}
};//补血丹策略类
class ItemStrategy_BXD : public ItemStrategy
{
public:virtual void UseItem(Fighter* mainobj){mainobj->SetLife(mainobj->GetLife() + 200); //补充200点生命值cout << "使用补血丹,生命值增加200。" << endl;}
};//大还丹策略类
class ItemStrategy_DHD : public ItemStrategy
{
public:virtual void UseItem(Fighter* mainobj){mainobj->SetLife(mainobj->GetLife() + 300); //补充300点生命值cout << "使用大还丹,生命值增加300。" << endl;}
};//守护丹策略类
class ItemStrategy_SHD : public ItemStrategy
{
public:virtual void UseItem(Fighter* mainobj){mainobj->SetLife(mainobj->GetLife() + 500); //补充500点生命值cout << "使用守护丹,生命值增加500。" << endl;}
};int main()
{// 创建战斗者对象Fighter* prole_war = new Fighter(1000, 0, 200);// 打印初始生命值cout << "初始生命值:" << prole_war->GetLife() << endl;// 使用大还丹ItemStrategy* strategy1 = new ItemStrategy_DHD();prole_war->SetItemStrategy(strategy1);prole_war->UseItem();cout << "当前生命值:" << prole_war->GetLife() << endl;// 使用补血丹ItemStrategy* strategy2 = new ItemStrategy_BXD();prole_war->SetItemStrategy(strategy2);prole_war->UseItem();cout << "当前生命值:" << prole_war->GetLife() << endl;// 使用守护丹ItemStrategy* strategy3 = new ItemStrategy_SHD();prole_war->SetItemStrategy(strategy3);prole_war->UseItem();cout << "当前生命值:" << prole_war->GetLife() << endl;// 释放资源delete strategy1;delete strategy2;delete strategy3;delete prole_war;F_Mage mage(800, 300, 100);cout << "法师初始生命值:" << mage.GetLife() << endl;// 可以为法师设置道具策略并使用// ...return 0;
}

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

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

相关文章

【c语言】整数在内存中的储存(大小端字节序)

整数在内存中的储存&#xff08;大小端字节序&#xff09; 1.整数在内存中的储存 2.大小端字节序 3.整数在内存中储存例子 4.字节序判断 5.死循环现象 文章目录 整数在内存中的储存&#xff08;大小端字节序&#xff09;整数在内存中的储存大小端字节序什么是大小端为什么会有…

Unity 麦扣 x 勇士传说 全解析 之 怪物基类(2)(附各模块知识的链接,零基础也包学会的牢弟)(案例难度:★★☆☆☆)

1.怪物的动画逻辑一览 2.怪物的受伤死亡逻辑一览 using System.Collections; using System.Collections.Generic; using System.Xml; using UnityEngine;public class Monster : MonoBehaviour {[Header("速度")]public float normalSpeed;public float chaseSpeed;…

在国产芯片上实现YOLOv5/v8图像AI识别-【2.5】yolov8使用C++部署在RK3588更多内容见视频

本专栏主要是提供一种国产化图像识别的解决方案&#xff0c;专栏中实现了YOLOv5/v8在国产化芯片上的使用部署&#xff0c;并可以实现网页端实时查看。根据自己的具体需求可以直接产品化部署使用。 B站配套视频&#xff1a;https://www.bilibili.com/video/BV1or421T74f 背景…

nginx简介及功能

一、简介&#xff1a; 1、nginx、apache是什么&#xff1f; ‌Nginx‌是一个高性能的HTTP和反向代理web服务器&#xff0c;同时也提供了IMAP/POP3/SMTP服务。它由伊戈尔赛索耶夫为Rambler.ru站点开发&#xff0c;以其稳定性、丰富的功能集、简单的配置文件和低系统资源的消耗而…

OSI七层网络模型 /TCP/IP五层模型以及封装分用的详细讲解

文章目录 协议分层的好处OSI七层网络模型TCP/IP五层网络模型网络设备所在的分层(重点)封装和分用 协议分层的好处 第一点&#xff1a; 在网络通信中&#xff0c;如果使用一个协议来解决所有的问题&#xff0c;那么这个协议就会非常的庞大&#xff0c;非常不利于去学习和理解&…

2023华为od机试C卷【转盘寿司】C 实现 单调栈

#include <stdio.h> #include <stdlib.h>/*单调栈 旋转寿司3 15 6 14 3 21 9 17*/ int main() {int i 0;int len 0;int data 0;int nums[501];char c ;while(scanf("%d",&nums[i]) 1){i;len;c getchar();if(c \n)break;}int *out NULL;int *s…

C语言-部分字符串函数详解 1-4

C语言-部分字符串函数详解 1-4 前言1.strlen1.1基本用法1.2注意事项\0size_t 1.3模拟实现 2.strcpy2.1基本用法2.2注意事项**源字符串必须以 \0 结束****会将源字符串中的 \0拷贝到目标空间****目标空间必须可修改****目标空间必须能容纳下源字符串的内容** 2.3模拟实现 3.strn…

【深度学习】【语音TTS】GPT-SoVITS v2 实战,训练一个人的音色,Docker镜像

文章目录 原理Dockerdocker push训练教程: https://www.yuque.com/baicaigongchang1145haoyuangong/ib3g1e/xyyqrfwiu3e2bgyk 原理 Docker 不用docker不行,不好分配显卡, 做个docker镜像: docker pull pytorch/pytorch:2.1.2

接口基础知识9:详解response body(响应体)

课程大纲 一、定义 HTTP响应体&#xff08;HTTP Response Body&#xff09;&#xff1a;服务器返回给客户端的数据部分&#xff0c;‌它包含了服务器对客户端请求的响应内容&#xff08;如客户端请求的资源、客户端请求的执行结果&#xff09;。 与请求类似&#xff0c;HTTP …

python之matplotlib (3 坐标轴设置)

写在前面 在说明坐标轴设置之前&#xff0c;我有必要和大家说清楚图像设置的一些方法&#xff0c;避免陷入困扰模糊的地步。前面我们说过&#xff0c;画图的三种方法&#xff08;python之matplotlib &#xff08;1 介绍及基本用法&#xff09;-CSDN博客&#xff09;。而设置也…

2024开源资产管理系统推荐 8款免费开源IT资产管理系统/软件

开源资产管理系统 开源资产管理系统是帮助企业管理、跟踪和优化其资产的强大工具。这些系统能够自动记录资产的详细信息&#xff0c;如采购日期、使用情况、维护记录等&#xff0c;从而实现资产的全生命周期管理。企业可以通过这些系统优化资产使用效率&#xff0c;减少资产闲…

【TCP】确认应答、超时重传机制和TCP报头

TCP 相关机制 TCP 基本特点&#xff1a;有连接、可靠传输、面向字节流、全双工 有连接、面向字节流和全双工都能在前面的代码中体现有连接&#xff1a;必须要先调用 accept 建立联系才能处理面向字节流&#xff1a;会拿到 clientSocket 对象的 InputStream 和 OutputStream&a…

python人工智能002:jupyter基本使用

小知识&#xff1a;将jupyter修改为中文&#xff0c;修改用户变量&#xff0c; 注意是用户变量&#xff0c;不是系统变量 新增用户变量 变量名&#xff1a;LANG 变量值&#xff1a;zh_CN.UTF8 然后重启jupyter 上一章的软件安装完成之后&#xff0c;就可以创建文件夹来学习写…

MaxKB(二):Ubuntu24.04搭建maxkb开发环境

接上文&#xff1a;windows10搭建maxkb开发环境&#xff08;劝退指南&#xff09; 上文在windows10环境搭建maxkb开发环境遇到各种坑&#xff0c;后面就转战ubuntu平台&#xff0c;果然比较顺利的完成开发环境搭建。当然遇到相关的问题还是可以参考上文《windows10搭建maxkb开发…

拟合与插值|线性最小二乘拟合|非线性最小二乘拟合|一维插值|二维插值

挖掘数据背后的规律是数学建模的重要任务&#xff0c;拟合与插值是常用的分析方法 掌握拟合与插值的基本概念和方法熟悉Matlab相关程序实现能够从数据中挖掘数学规律 拟合问题的基本提法 拟合问题的概念 已知一组数据(以二维为例)&#xff0c;即平面上n个点 ( x i , y i ) …

C语言指针详解-上

C语言指针详解-上 前言1.指针的基本概念1.1指针是什么1.2指针的声明与初始化1.3取地址符&和解引用符*& 运算符用于**获取变量的地址*** 运算符用于访问指针指向的值 2.指针的类型常见数据类型的指针指针与数组、字符串数组指针结构体指针函数指针二级指针void指针 3.指…

对零基础想转行网络安全同学的一点建议

最近有同学在后台留言&#xff0c;0基础怎么学网络安全&#xff1f;0基础可以转行做网络安全吗&#xff1f;以前也碰到过类似的问题&#xff0c;想了想&#xff0c;今天简单写一下。 我的回答是先了解&#xff0c;再入行。 具体怎么做呢&#xff1f; 首先&#xff0c;你要确…

滑动变阻器的未来发展趋势和前景如何?是否有替代品出现?

滑动变阻器是常见的电子元件&#xff0c;主要用于调节电路中的电阻值。随着科技的不断发展&#xff0c;滑动变阻器的未来发展趋势和前景也引起了广泛关注。 滑动变阻器的未来发展将更加注重智能化&#xff0c;随着物联网、人工智能等技术的快速发展&#xff0c;滑动变阻器也将与…

C语言 | Leetcode C语言题解之第347题前K个高频元素

题目&#xff1a; 题解&#xff1a; struct hash_table {int key;int val;// 查看 https://troydhanson.github.io/uthash/ 了解更多UT_hash_handle hh; };typedef struct hash_table* hash_ptr;struct pair {int first;int second; };void swap(struct pair* a, struct pair*…

YUM和NFS

文章目录 yum软件仓库的提供方式RPM软件包的来源Linux系统各家厂商用的安装源命令---yum 配置本地yum源具体操作 搭建ftp yum仓库环境具体操作实操环境服务端一、安装 vsftpd服务二、创建一个文件&#xff0c;并且挂载三、开启服务四、查看挂载 客户端五、备份六、搭建ftp yum仓…