当前位置: 首页 > news >正文

设计模式--命令模式的简单例子

引入:以一个对数组的增删改查为例。通过命令模式可以对数组进行增删改查以及撤销回滚。

一、基本概念

命令模式有多种分法,在本文中主要分为CommandMgr、Command、Receiver.

CommandMgr主要用于控制命令执行等操作、Command为具体的命令、Receiver为命令具体要操作的对象。

总而言之,增删改查就是具体的Command、Receiver就是数组、CommandMgr负责控制命令的执行与回滚等。

二、程序设计

以下代码可从github下载:GitHub - laizilianglaiziliang/LearnCommandProject

1.Receiver
//"Receiver_Array.h"
#pragma once
#include<vector>
#include<optional>
#include<iostream>
template <class T>
class Receiver_Array
{
private:std::vector<T>* myArry;
public:~Receiver_Array(){}Receiver_Array() {myArry = new std::vector<T>();}Receiver_Array(std::vector<T>* _myArry){if (_myArry){myArry = new std::vector<T>(*_myArry);}else{myArry = new std::vector<T>();}}bool add(const int pos, const T& val){if (errorCheck(pos)){return false;}myArry->insert(pos + myArry->begin(), val);return true;}bool del(const int pos){if (errorCheck(pos)){return false;}myArry->erase(pos + myArry->begin());return true;}bool modify(const int pos, const T& val){if (errorCheck(pos)){return false;}myArry->erase(pos + myArry->begin());return true;}std::optional<T> seek(const int pos){if (errorCheck(pos)){return std::nullopt;}return (*myArry)[pos];}bool errorCheck(const int pos){if (pos >= myArry->size()){printf("  Operation Failed.Array Bounds Errors.  ");return true;}return false;}void display(){for (int i = 0; i < myArry->size(); ++i){std::cout << (*myArry)[i] << "  ";}std::cout << std::endl;}
};

在本例子中Receiver_Array类是一个模板类,可支持不同类型的数组。同时实现了对数组进行增删改查,为不同的命令类提供了基础的功能。

2.Command
//Command.h
#pragma once
#include "Receiver_Array.h"
#include<optional>
#include <memory>
class Command
{
public:		int m_iPos;bool m_bCanRevert;
public:Command(int _pos) : m_iPos(_pos), m_bCanRevert(true){}virtual ~Command(){}virtual bool  execute() = 0;virtual void* executeOther(){return nullptr;}virtual bool  undo() = 0;virtual bool  redo() = 0;
};
template <class T>
class AddCommand:public Command
{
private:std::shared_ptr<Receiver_Array<T>> m_Receiver_Array;T m_Val;
public:AddCommand() {}AddCommand(std::shared_ptr<Receiver_Array<T>> _receiver_Array, int _pos,const T& _val) :Command( _pos), m_Receiver_Array(_receiver_Array), m_Val(_val){		}virtual ~AddCommand() {//if (m_Receiver_Array)//{//	m_Receiver_Array->destory();//	m_Receiver_Array = nullptr;//}}bool execute() override{try{if (this->m_Receiver_Array->add(this->m_iPos, m_Val)){printf("  Add Success.");return true;}printf("  Add Fail.");return false;}catch(...){printf("  Add Fail.");return false;}return true; }virtual bool undo() override{try{if (this->m_Receiver_Array->del(this->m_iPos)){printf("  Undo Success.");return true;}}catch (...){}printf("  Undo Fail.");return false;}virtual bool redo() override{if (execute()){printf("  Redo Success.");return true;}printf("  Redo Fail.");return false;}
};
template <class T>
class DelCommand :public Command
{
private:std::shared_ptr<Receiver_Array<T>>  m_Receiver_Array;T m_Val;
public:DelCommand() {}DelCommand(std::shared_ptr<Receiver_Array<T>>  _receiver_Array, int _pos) :Command(_pos), m_Receiver_Array(_receiver_Array){}virtual ~DelCommand(){//if (m_Receiver_Array)//{//	m_Receiver_Array->destory();//	m_Receiver_Array = nullptr;//}}bool execute() override{try{std::optional<T> val = m_Receiver_Array->seek(m_iPos);if (val!=std::nullopt && this->m_Receiver_Array->del(this->m_iPos)){printf("  Del Success.");m_Val = val.value();return true;}printf("  Del Fail.");return false;}catch (...){printf("  Del Fail.");return false;}return true;}virtual bool undo() override{try{if (this->m_Receiver_Array->add(this->m_iPos, m_Val)){printf("  Undo Success.");return true;}}catch (...){}printf("  Undo Fail.");return false;}virtual bool redo() override{if (execute()){printf("  Redo Success.");return true;}printf("  Redo Fail.");return false;}
};
template <class T>
class ModifyCommand :public Command
{
private:std::shared_ptr<Receiver_Array<T>>  m_Receiver_Array;T m_Val;
public:ModifyCommand() {}ModifyCommand(std::shared_ptr<Receiver_Array<T>>  _receiver_Array, int _pos,const T& _val) :Command(_pos), m_Receiver_Array(_receiver_Array), m_Val(_val){}virtual ~ModifyCommand(){//if (m_Receiver_Array)//{//	m_Receiver_Array->destory();//	m_Receiver_Array = nullptr;//}}bool execute() override{try{std::optional<T> val = this->m_Receiver_Array->seek(m_iPos);//判断m_iPos是合法的if (val != std::nullopt && this->m_Receiver_Array->modify(this->m_iPos, m_Val)){printf("  Modify Success.");m_Val = val.has_value();//用于undo redoreturn true;}			printf("  Modify Fail.");return false;}catch (...){printf("  Modify Fail.");return false;}return true;}virtual bool undo() override{try{if (execute()){printf("  Undo Success.");return true;}}catch (...){}printf("  Undo Fail.");return false;}virtual bool redo() override{if (execute()){printf("  Redo Success.");return true;}printf("  Redo Fail.");return false;}
};
template <class T>
class SeekCommand :public Command
{
private:std::shared_ptr<Receiver_Array<T>>  m_Receiver_Array;
public:SeekCommand():m_bCanRevert(false) {}SeekCommand(std::shared_ptr<Receiver_Array<T>>  _receiver_Array, int _pos) :Command(_pos), m_Receiver_Array(_receiver_Array){m_bCanRevert = false;//, m_bCanRevert(false)}virtual ~SeekCommand(){//if (m_Receiver_Array)//{//	m_Receiver_Array->destory();//	m_Receiver_Array = nullptr;//}}bool execute() override{return false;}virtual void* executeOther() override{try{std::optional<T> val = m_Receiver_Array->seek(m_iPos);if (val == std::nullopt){printf("  Seek Fail.");return nullptr;}printf("  Seek Success.");T* ret = new T();*ret = val.value();return ret;}catch (...){}printf("  Seek Fail.");return nullptr;}virtual bool undo() override{printf("  Undo Fail.");return false;}virtual bool redo() override{printf("  Redo Fail.");return false;}
};

1)Command类是命令基类。本来也想将Command设计成模板类,但是后面想想感觉不太好,因为Command设计成模板类会影响到CommandMgr也变成模板类。如果Command类是模板类,要注意其属性如果在派生类中要用的话要用this指针去访问,否则会出现找不到标识符的问题。

可参考:

C++模板类中,派生类使用基类中数据或方法报“找不到标识符”_c++头文件引用其他类提示找不到符号-CSDN博客

2)Command类中有个m_bCanRevert属性用于判断该命令是否可以被撤销回滚,因为并不是所有的命令都支持撤销回滚,比如例子中的SeekCommand。

3)Command类中有个executeOther,因为SeekCommand执行后需要返回一个值,是特殊的命令,因此executeOther用于执行特殊的命令

4)其他的Command派生类依赖于Receiver_Array类,可能会出现多个类依赖于同一个Receiver_Array类对象的情况,因此把Receiver_Array类成员变量设置为智能指针方便内存的释放

5)其他的主要就是实现每个Command类的execute、undo、redo方法,这个直接看逻辑就能理解。

3.CommandMgr
//CommandMgr.h
#pragma once
#include <stack>
#include <memory>
class Command;
class CommandMgr
{
private:std::stack<std::shared_ptr<Command>> m_stkUndo;//undo栈std::stack<std::shared_ptr<Command>> m_stkRedo;//redo栈
public:CommandMgr();~CommandMgr();void execute(std::shared_ptr<Command> command) noexcept;void* executeOther(std::shared_ptr<Command> command)noexcept;void undo() noexcept;void redo() noexcept;
};
//CommandMgr.cpp
#include "CommandMgr.h"
#include "Command.h"
CommandMgr::CommandMgr()
{
}
CommandMgr::~CommandMgr()
{while (!m_stkRedo.empty()){m_stkRedo.pop();}while (!m_stkUndo.empty()){		m_stkUndo.pop();}
}
void CommandMgr::execute(std::shared_ptr<Command> command) noexcept
{if (command->execute()){printf("  Command Execute Success\n\n");if (command->m_bCanRevert){m_stkUndo.push(command);}}else{printf("  Command Execute Fail\n\n");}
}void* CommandMgr::executeOther(std::shared_ptr<Command> command) noexcept
{void* val = command->executeOther();if (val){printf("  Command Execute Success\n\n");if (command->m_bCanRevert){m_stkUndo.push(command);}return val;}else{printf("  Command Execute Fail\n\n");}return nullptr;
}void CommandMgr::undo() noexcept
{if (m_stkUndo.empty()){return;}std::shared_ptr<Command> command = m_stkUndo.top();if (command && command->m_bCanRevert && command->undo()){		m_stkUndo.pop();m_stkRedo.push(command);printf("  Command Undo Success\n\n");}else{printf("  Command Undo Fail\n\n");}
}void CommandMgr::redo() noexcept
{if (m_stkRedo.empty()){return;}std::shared_ptr<Command> command = m_stkRedo.top();if (command && command->m_bCanRevert && command->redo()){m_stkUndo.push(command);printf("  Command Redo Success\n\n");}else{printf("  Command Redo Fail\n\n");}
}

1)CommandMgr主要用于管理命令,用来操作具体的命令的调用控制、undo、redo控制。

2)因为Command类型对象的内存不太好管理,因此也使用了智能指针。

3)里面的undo、redo主要通过栈来实现。当命令execute过后便会添加到undo栈,接下来的undo、redo主要就是对undo栈和redo栈进行互相倒腾。

4.main函数

当做完上面的工作就能对数组进行方便的增删改查了,还可以撤销回退哦。

// LearnCommandProject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。#include <iostream>
#include <vector>
#include "Command.h"
#include "Receiver_Array.h"
#include "CommandMgr.h"
#include <memory>
int main()
{std::vector<int> vec{ 1,2,3 };std::shared_ptr<Receiver_Array<int>> receiver_Array(new Receiver_Array<int>(&vec));std::shared_ptr<AddCommand<int>> addCommand(new AddCommand<int>(receiver_Array, 3, 4));CommandMgr commandMgr;commandMgr.execute(addCommand);commandMgr.undo();commandMgr.redo();receiver_Array->display();std::shared_ptr<SeekCommand<int>> seekCommand(new SeekCommand<int>(receiver_Array, 1));int* val= (int*)(commandMgr.executeOther(seekCommand));receiver_Array->display();delete val;val = nullptr;std::shared_ptr<DelCommand<int>> delCommand(new DelCommand(receiver_Array, 1));commandMgr.execute(delCommand);commandMgr.undo();commandMgr.redo();receiver_Array->display();std::shared_ptr<ModifyCommand<int>> modifyCommand(new ModifyCommand(receiver_Array, 2, 2));commandMgr.execute(modifyCommand);commandMgr.undo();commandMgr.redo();receiver_Array->display();printf("ok");
}

.上面的代码可能还有设计不好的地方,欢迎批评指正。

相关文章:

设计模式--命令模式的简单例子

引入&#xff1a;以一个对数组的增删改查为例。通过命令模式可以对数组进行增删改查以及撤销回滚。 一、基本概念 命令模式有多种分法&#xff0c;在本文中主要分为CommandMgr、Command、Receiver. CommandMgr主要用于控制命令执行等操作、Command为具体的命令、Receiver为命…...

排序算法之六:快速排序(非递归)

快速排序是非常适合使用递归的&#xff0c;但是同时我们也要掌握非递归的算法 因为操作系统的栈空间很小&#xff0c;如果递归的深度太深&#xff0c;容易造成栈溢出 递归改非递归一般有两种改法&#xff1a; 改循环借助栈&#xff08;数据结构&#xff09; 图示算法 不是…...

【概率方法】重要性采样

从一个极简分布出发 假设我们有一个关于随机变量 X X X 的函数 f ( X ) f(X) f(X)&#xff0c;满足如下分布 p ( X ) p(X) p(X)0.90.1 f ( X ) f(X) f(X)0.10.9 如果我们要对 f ( X ) f(X) f(X) 的期望 E p [ f ( X ) ] \mathbb{E}_p[f(X)] Ep​[f(X)] 进行估计&#xff0…...

MyBatis 四大核心组件之 StatementHandler 源码解析

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…...

用Guava做本地缓存示例

缓存的作用 提升系统性能&#xff0c;暂时在内存中保存业务系统的数据处理结果&#xff0c;并且等待下次访问使用 本地缓存和分布式缓存 缓存分为本地缓存与分布式缓存。本地缓存为了保证线程安全问题&#xff0c;一般使用ConcurrentMap的方式保存在内存之中&#xff0c;而常…...

Django多对多ManyToManyField字段

Django是一个支持多对多关系的Web框架&#xff0c;可以在模型中定义多对多关系。多对多关系通常涉及两个实体之间的复杂交互&#xff0c;例如用户和组之间的关系&#xff0c;或者课程和学生之间的关系。在Django中&#xff0c;可以使用ManyToManyField字段来定义多对多关系。 …...

docker-centos中基于keepalived+niginx模拟主从热备完整过程

文章目录 一、环境准备二、主机1、环境搭建1.1 镜像拉取1.2 创建网桥1.3 启动容器1.4 配置镜像源1.5 下载工具包1.6 下载keepalived1.7 下载nginx 2、配置2.1 配置keepalived2.2 配置nginx2.2.1 查看nginx.conf2.2.2 修改index.html 3、启动3.1 启动nginx3.2 启动keepalived 4、…...

软件科技成果鉴定测试需提供哪些材料?

为了有效评估科技成果的质量&#xff0c;促进科技理论向实际应用转化&#xff0c;所以需要进行科技成果鉴定测试。申请鉴定的科技成果范围是指列入国家和省、自治区、直辖市以及国务院有关部门科技计划内的应用技术成果&#xff0c;以及少数科技计划外的重大应用技术成果。   …...

办公word-从不是第一页添加页码

总结 实际需要注意的是&#xff0c;分隔符、分节符和分页符并不是一个含义 分隔符包含其他两个&#xff1b;分页符&#xff1a;是增加一页&#xff1b;分节符&#xff1a;指将文档分为几部分。 从不是第一页插入页码1步骤 1&#xff0c;插入默认页码 自己可以测试时通过**…...

Android笔记(十七):PendingIntent简介

PendingIntent翻译成中文为“待定意图”&#xff0c;这个翻译很好地表示了它的涵义。PendingIntent描述了封装Intent意图以及该意图要执行的目标操作。PendingIntent封装Intent的目标行为的执行是必须满足一定条件&#xff0c;只有条件满足&#xff0c;才会触发意图的目标操作。…...

为 Compose MultiPlatform 添加 C/C++ 支持(2):在 jvm 平台使用 jni 实现桌面端与 C/C++ 互操作

前言 在上篇文章中我们已经介绍了实现 Compose MultiPlatform 对 C/C 互操作的基本思路。 并且先介绍了在 kotlin native 平台使用 cinterop 实现与 C/C 的互操作。 今天这篇文章将补充在 jvm 平台使用 jni。 在 Compose MultiPlatform 中&#xff0c;使用 jvm 平台的是 An…...

【PyTorch】卷积神经网络

文章目录 1. 理论介绍1.1. 从全连接层到卷积层1.1.1. 背景1.1.2. 从全连接层推导出卷积层 1.2. 卷积层1.2.1. 图像卷积1.2.2. 填充和步幅1.2.3. 多通道 1.3. 池化层&#xff08;又称汇聚层&#xff09;1.3.1. 背景1.3.2. 池化运算1.3.3. 填充和步幅1.3.4. 多通道 1.4. 卷积神经…...

qt可以详细写的项目或技术

1.QT 图形视图框架 2.QT 模型视图结构 3.QT列表显示大量信息 4.QT播放器 5.QT 编解码 6.QT opencv...

操作系统笔记——储存系统、文件系统(王道408)

文章目录 前言储存系统地址转换内存扩展覆盖交换 储存器分配——连续分配固定大小分区动态分区分配动态分区分配算法 储存器分配——非连续分配页式管理基本思想地址变换硬件快表&#xff08;TLB&#xff09;多级页表 段式管理段页式管理 虚拟储存器——基于交换的内存扩充技术…...

基于Html+腾讯云播SDK开发的m3u8播放器

周末业余时间在家无事&#xff0c;学习了一下腾讯的云播放sdk&#xff0c;并制作了一个小demo&#xff08;m3u8播放器&#xff09;&#xff0c;该在线工具是基于腾讯的云播sdk开发的&#xff0c;云播sdk非常牛&#xff0c;可以支持多种播放格式。 预览地址 m3u8player.org 源码…...

uniapp小程序分享为灰色

引用&#xff1a;https://www.cnblogs.com/panwudi/p/17074172.html uniapp开发的微信小程序&#xff0c;没有转发&#xff0c;分享&#xff1a; 创建一个mixin:common/share.js export default {onShareAppMessage(res) { //发送给朋友return {}},onShareTimeline(res) {//…...

python:五种算法(OOA、WOA、GWO、PSO、GA)求解23个测试函数(python代码)

一、五种算法简介 1、鱼鹰优化算法OOA 2、鲸鱼优化算法WOA 3、灰狼优化算法GWO 4、粒子群优化算法PSO 5、遗传算法GA 二、5种算法求解23个函数 &#xff08;1&#xff09;23个函数简介 参考文献&#xff1a; [1] Yao X, Liu Y, Lin G M. Evolutionary programming made…...

DIP——添加运动模糊与滤波

1.运动模糊 为了模拟图像退化的过程&#xff0c;在这里创建了一个用于模拟运动模糊的点扩散函数&#xff0c;具体模糊的方向取决于输入的motion_angle。如果运动方向接近水平&#xff0c;则模糊效果近似水平&#xff0c;如果运动方向接近垂直&#xff0c;则模糊效果近似垂直。具…...

SQL Server查询计划(Query Plan)——SQL处理过程

6. 查询计划(Query Plan) 6.1. SQL处理过程 就SQL语句的处理过程而言,各关系库间大同小异,尤其是商业库之间实现机制和细节差别更小些,其功能及性能支持方面也更加强大和完善。SQL Server作为商业库中的后起之秀,作为SQL语句处理过程的主要支撑和保障,其优化器及相关机…...

【动手学深度学习】(十二)现代卷积神经网络

文章目录 一、深度卷积神经网络AlexNet1.理论知识 一、深度卷积神经网络AlexNet 1.理论知识 ImageNet(2010) 图片自然物体的彩色图片手写数字的黑色图片大小468 * 38728*28样本数1.2M60K类数100010 AlexNet AlexNet赢了2012ImageNet竞赛更深更大的LeNet主要改进&#xff…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

uniapp 字符包含的相关方法

在uniapp中&#xff0c;如果你想检查一个字符串是否包含另一个子字符串&#xff0c;你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的&#xff0c;但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor

1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...

CVE-2023-25194源码分析与漏洞复现(Kafka JNDI注入)

漏洞概述 漏洞名称&#xff1a;Apache Kafka Connect JNDI注入导致的远程代码执行漏洞 CVE编号&#xff1a;CVE-2023-25194 CVSS评分&#xff1a;8.8 影响版本&#xff1a;Apache Kafka 2.3.0 - 3.3.2 修复版本&#xff1a;≥ 3.4.0 漏洞类型&#xff1a;反序列化导致的远程代…...

运行vue项目报错 errors and 0 warnings potentially fixable with the `--fix` option.

报错 找到package.json文件 找到这个修改成 "lint": "eslint --fix --ext .js,.vue src" 为elsint有配置结尾换行符&#xff0c;最后运行&#xff1a;npm run lint --fix...

生信服务器 | 做生信为什么推荐使用Linux服务器?

原文链接&#xff1a;生信服务器 | 做生信为什么推荐使用Linux服务器&#xff1f; 一、 做生信为什么推荐使用服务器&#xff1f; 大家好&#xff0c;我是小杜。在做生信分析的同学&#xff0c;或是将接触学习生信分析的同学&#xff0c;<font style"color:rgb(53, 1…...