以绘图(绘制点、直线、圆、椭圆、多段线)为例子 通过设计模式中的命令模式实现
为了在命令模式的基础上实现撤销(Undo)和回退(Redo)功能,我们可以在每个命令类中记录一些必要的状态,允许我们撤销之前的操作,并在需要时回退操作。常见的做法是使用一个命令堆栈来存储历史命令,并为每个命令提供撤销(undo)操作。
我们可以通过以下步骤来添加撤销和回退功能:
- Command接口:为命令接口添加一个
undo()方法。 - 具体命令类(ConcreteCommand):为每个具体命令类实现
undo()方法,撤销相应的操作。 - Invoker类:管理一个命令堆栈(历史记录),并实现
undo()和redo()方法来执行撤销和回退操作。
代码实现:
我们将对之前的代码进行修改,来实现撤销和回退功能:
#include <iostream>
#include <memory>
#include <vector>
#include <stack>// 绘图命令接口
class Command {
public:virtual ~Command() = default;virtual void execute() = 0;virtual void undo() = 0; // 增加撤销操作接口
};// 接收者:绘图工具(如画布)
class Receiver {
public:void drawPoint(int x, int y) {std::cout << "Drawing point at (" << x << ", " << y << ").\n";}void undoDrawPoint(int x, int y) {std::cout << "Undo drawing point at (" << x << ", " << y << ").\n";}void drawLine(int x1, int y1, int x2, int y2) {std::cout << "Drawing line from (" << x1 << ", " << y1 << ") to (" << x2 << ", " << y2 << ").\n";}void undoDrawLine(int x1, int y1, int x2, int y2) {std::cout << "Undo drawing line from (" << x1 << ", " << y1 << ") to (" << x2 << ", " << y2 << ").\n";}void drawCircle(int x, int y, int radius) {std::cout << "Drawing circle at (" << x << ", " << y << ") with radius " << radius << ".\n";}void undoDrawCircle(int x, int y, int radius) {std::cout << "Undo drawing circle at (" << x << ", " << y << ") with radius " << radius << ".\n";}void drawEllipse(int x, int y, int majorAxis, int minorAxis) {std::cout << "Drawing ellipse at (" << x << ", " << y << ") with major axis " << majorAxis << " and minor axis " << minorAxis << ".\n";}void undoDrawEllipse(int x, int y, int majorAxis, int minorAxis) {std::cout << "Undo drawing ellipse at (" << x << ", " << y << ") with major axis " << majorAxis << " and minor axis " << minorAxis << ".\n";}void drawPolyline(const std::vector<std::pair<int, int>>& points) {std::cout << "Drawing polyline with the following points:\n";for (const auto& point : points) {std::cout << "(" << point.first << ", " << point.second << ") ";}std::cout << "\n";}void undoDrawPolyline(const std::vector<std::pair<int, int>>& points) {std::cout << "Undo drawing polyline with the following points:\n";for (const auto& point : points) {std::cout << "(" << point.first << ", " << point.second << ") ";}std::cout << "\n";}
};// 绘制点的命令
class DrawPointCommand : public Command {
private:Receiver* receiver;int x, y;
public:DrawPointCommand(Receiver* r, int x, int y) : receiver(r), x(x), y(y) {}void execute() override {receiver->drawPoint(x, y);}void undo() override {receiver->undoDrawPoint(x, y);}
};// 绘制直线的命令
class DrawLineCommand : public Command {
private:Receiver* receiver;int x1, y1, x2, y2;
public:DrawLineCommand(Receiver* r, int x1, int y1, int x2, int y2) : receiver(r), x1(x1), y1(y1), x2(x2), y2(y2) {}void execute() override {receiver->drawLine(x1, y1, x2, y2);}void undo() override {receiver->undoDrawLine(x1, y1, x2, y2);}
};// 绘制圆形的命令
class DrawCircleCommand : public Command {
private:Receiver* receiver;int x, y, radius;
public:DrawCircleCommand(Receiver* r, int x, int y, int radius) : receiver(r), x(x), y(y), radius(radius) {}void execute() override {receiver->drawCircle(x, y, radius);}void undo() override {receiver->undoDrawCircle(x, y, radius);}
};// 绘制椭圆的命令
class DrawEllipseCommand : public Command {
private:Receiver* receiver;int x, y, majorAxis, minorAxis;
public:DrawEllipseCommand(Receiver* r, int x, int y, int majorAxis, int minorAxis) : receiver(r), x(x), y(y), majorAxis(majorAxis), minorAxis(minorAxis) {}void execute() override {receiver->drawEllipse(x, y, majorAxis, minorAxis);}void undo() override {receiver->undoDrawEllipse(x, y, majorAxis, minorAxis);}
};// 绘制多段线的命令
class DrawPolylineCommand : public Command {
private:Receiver* receiver;std::vector<std::pair<int, int>> points;
public:DrawPolylineCommand(Receiver* r, const std::vector<std::pair<int, int>>& points) : receiver(r), points(points) {}void execute() override {receiver->drawPolyline(points);}void undo() override {receiver->undoDrawPolyline(points);}
};// 调用者:工具栏或按钮
class Invoker {
private:std::stack<std::shared_ptr<Command>> commandHistory; // 历史命令栈std::stack<std::shared_ptr<Command>> redoStack; // 重做命令栈public:void executeCommand(std::shared_ptr<Command> cmd) {cmd->execute();commandHistory.push(cmd); // 将命令压入历史栈while (!redoStack.empty()) { // 清空重做栈redoStack.pop();}}void undo() {if (!commandHistory.empty()) {std::shared_ptr<Command> cmd = commandHistory.top();commandHistory.pop();cmd->undo();redoStack.push(cmd); // 将撤销的命令压入重做栈} else {std::cout << "No command to undo.\n";}}void redo() {if (!redoStack.empty()) {std::shared_ptr<Command> cmd = redoStack.top();redoStack.pop();cmd->execute();commandHistory.push(cmd); // 将重做的命令压入历史栈} else {std::cout << "No command to redo.\n";}}
};// 客户端代码
int main() {Receiver receiver; // 绘图工具(画布)// 创建具体的命令std::shared_ptr<Command> drawPoint = std::make_shared<DrawPointCommand>(&receiver, 10, 20);std::shared_ptr<Command> drawLine = std::make_shared<DrawLineCommand>(&receiver, 10, 20, 30, 40);std::shared_ptr<Command> drawCircle = std::make_shared<DrawCircleCommand>(&receiver, 50, 50, 15);std::shared_ptr<Command> drawEllipse = std::make_shared<DrawEllipseCommand>(&receiver, 70, 70, 20, 10);std::vector<std::pair<int, int>> polylinePoints = {{10, 10}, {20, 20}, {30, 30}, {40, 40}};std::shared_ptr<Command> drawPolyline = std::make_shared<DrawPolylineCommand>(&receiver, polylinePoints);// 创建调用者Invoker invoker;// 模拟用户操作,通过调用命令绘制图形invoker.executeCommand(drawPoint);invoker.executeCommand(drawLine);invoker.executeCommand(drawCircle);invoker.executeCommand(drawEllipse);invoker.executeCommand(drawPolyline);// 撤销操作std::cout << "\nUndoing the last command:\n";invoker.undo();// 回退(重做)操作std::cout << "\nRedoing the last undone command:\n";invoker.redo();return 0;
}
关键修改:
- Command接口:添加了
undo()方法,使每个命令都能撤销其操作。 - Receiver类:为每个绘制方法添加了撤销方法(
undoDraw...),用于撤销具体的图形操作。 - Invoker类:管理两个栈——
commandHistory(历史命令栈)和redoStack(重做命令栈)。在执行命令时将其压入commandHistory,在撤销时将命令从commandHistory中取出并执行undo(),同时将命令压入redoStack。回退时从redoStack取出命令并重新执行。
输出:
Drawing point at (10, 20).
Drawing line from (10, 20) to (30, 40).
Drawing circle at (50, 50) with radius 15.
Drawing ellipse at (70, 70) with major axis 20 and minor axis 10.
Drawing polyline with the following points:
(10, 10) (20, 20) (30, 30) (40, 40)Undoing the last command:
Undo drawing polyline with the following points:
(10, 10) (20, 20) (30, 30) (40, 40)Redoing the last undone command:
Drawing polyline with the following points:
(10, 10) (20, 20) (30, 30) (40, 40)
功能扩展:
- 撤销操作:允许撤销最后的绘图命令。
- 回退操作:允许重做之前撤销的命令。
这样,我们就实现了撤销和回退功能,用户可以随时撤销之前的操作并恢复它们。
相关文章:
以绘图(绘制点、直线、圆、椭圆、多段线)为例子 通过设计模式中的命令模式实现
为了在命令模式的基础上实现撤销(Undo)和回退(Redo)功能,我们可以在每个命令类中记录一些必要的状态,允许我们撤销之前的操作,并在需要时回退操作。常见的做法是使用一个命令堆栈来存储历史命令…...
鹏哥c语言数组(初阶数组)
前言: 对应c语言视频54集 内容: 一维数组的创建 数组是一组相同元素的集合, 数组的创建方式 type_t就是数组的元素类型,const_n是一个常量表达式,用来指定数组的大小 c99标准之前的,数组的大小必须是…...
利用go-migrate实现MySQL和ClickHouse的数据库迁移
1. 背景 在使用gorm时 , 尽管已经有了自动建表和钩子函数 . 但是在面临希望了解到数据库的变更 , 和插入一些系统字段时 , 以及最关键的数据库迁移的工作 . gorm显得稍微有点不便 . 在了解到migrate这项技术后 , 就使用go-migrate开发了一个可以迁移MySQL和ClickHouse数据库的…...
计算机毕业设计SpringBoot+Vue.js企业客户管理系统(源码+LW文档+PPT+讲解+开题报告)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
jmeter 如何做移动端的测试 特别是兼容性测试
JMeter本身主要是一款用于性能测试和功能测试的工具,虽然它并非专门为移动端测试设计,但可以通过一些方式来对移动端应用进行测试,以下从测试准备、测试过程及注意事项等方面为你详细介绍: 一、测试准备 (一)环境搭建 JMeter安装与配置:确保JMeter已经正确安装在测试机…...
深度学习技术全景图:从基础架构到工业落地的超级进化指南
🔍 目录导航 基础架构革命训练优化秘技未来战场前瞻 🧩 一、基础架构革命 1.1 前馈神经网络(FNN) ▍核心结构 import torch.nn as nnclass FNN(nn.Module):def __init__(self):super().__init__()self.fc1 nn.Linear(784, 25…...
vllm部署LLM(qwen2.5,llama,deepseek)
目录 环境 qwen2.5-1.5b-instruct 模型下载 vllm 安装 验证安装 vllm 启动 查看当前模型列表 OpenAI Completions API(文本生成) OpenAI Chat Completions API(chat 对话) vllm 进程查看,kill llama3 deep…...
基于SpringBoot的“古城景区管理系统”的设计与实现(源码+数据库+文档+PPT)
基于SpringBoot的“古城景区管理系统”的设计与实现(源码数据库文档PPT) 开发语言:Java 数据库:MySQL 技术:SpringBoot 工具:IDEA/Ecilpse、Navicat、Maven 系统展示 系统整体功能图 系统首页界面 系统注册界面 景…...
如何防止 Docker 注入了恶意脚本
根据您的描述,攻击者通过 CentOS 7 系统中的 Docker 注入了恶意脚本,导致自动启动名为 “masscan” 和 “x86botnigletjsw” 的进程。这些进程可能用于网络扫描或其他恶意活动。为了解决这一问题,建议您采取以下步骤: 1. 停止并删…...
使用python接入腾讯云DeepSeek
本文主要从提供SSE方式接入DeepSeek,并通过fastapi websocket对外提供接入方法。 参考文档: 腾讯云大模型:https://cloud.tencent.com/document/product/1759/109380 fastAPI官网:https://fastapi.tiangolo.com/ WebSocketManager…...
【MySQL】服务正在启动或停止中,请稍候片刻后再试一次【解决方案】
问题呈现 在使用MySQL的过程中我们可能会遇到以上的情况 解决方法 首先以管理员身份打开命令行窗口,注意是管理员身份,不然无权限访问。输入命令tasklist| findstr "mysql",用于查找mysql的残留进程。这个时候我们就会看到一个…...
测试工程师玩转DeepSeek之Prompt
以下是测试工程师使用DeepSeek的必知必会提示词指南,分为核心场景和高效技巧两大维度: 一、基础操作提示模板 1. 测试用例生成 "作为[金融系统/物联网设备/云服务]测试专家,请为[具体功能模块]设计测试用例,要求࿱…...
【PyTorch】2024保姆级安装教程-Python-(CPU+GPU详细完整版)-
一、准备工作 pytorch需要python3.6及以上的python版本 我是利用Anaconda来管理我的python。可自行安装Anaconda。 Anaconda官网 Free Download | Anaconda 具体Anaconda安装教程可参考 https://blog.csdn.net/weixin_43412762/article/details/129599741?fromshareblogdet…...
精选案例展 | 智己汽车—全栈可观测驱动智能化运营与成本优化
本案例为“观测先锋 2024 可观测平台创新应用案例大赛”精选案例,同时荣获IT168“2024技术卓越奖评选-年度创新解决方案”奖。 项目背景 近年来,中国汽车行业进入转型升级阶段,智能网联技术成为行业发展的核心。车联网、自动驾驶等技术的加速…...
MySQL 使用 `WHERE` 子句时 `COUNT(*)`、`COUNT(1)` 和 `COUNT(column)` 的区别解析
文章目录 1. COUNT() 函数的基本作用2. COUNT(*)、COUNT(1) 和 COUNT(column) 的详细对比2.1 COUNT(*) —— 统计所有符合条件的行2.2 COUNT(1) —— 统计所有符合条件的行2.3 COUNT(column) —— 统计某一列非 NULL 的记录数 3. 性能对比3.1 EXPLAIN 分析 4. 哪种方式更好&…...
Linux运维——网络管理
Linux网络管理 一、Linux网络应用要点二、命令常见用法2.1、curl2.1.1、发送GET请求2.1.2、发送POST请求2.1.3、设置请求头2.1.4、处理cookies2.1.5、处理重定向2.1.6、调试和详细信息2.1.7、使用代理2.1.8、文件上传2.1.9、其它常用选项2.1.10、综合示例 2.2、wget2.2.1、基本…...
STM32CUBEIDE FreeRTOS操作教程(十三):task api 任务访问函数
STM32CUBEIDE FreeRTOS操作教程(十三):task api 任务访问函数 STM32CUBE开发环境集成了STM32 HAL库进行FreeRTOS配置和开发的组件,不需要用户自己进行FreeRTOS的移植。这里介绍最简化的用户操作类应用教程。以STM32F401RCT6开发板…...
Jmeter+Jenkins接口压力测试持续集成
项目介绍 接口功能测试应用: http://www.weather.com.cn/data/cityinfo/<city_code>.html 测试功能:获取对应城市的天气预报 请求方法:Get 压测脚本开发工具:jmeter 源码脚本位置: https://github.com/shife…...
深入浅出ES6:现代JavaScript的基石
ES6(ECMAScript 2015)是JavaScript语言的一次重大更新,引入了许多新特性,使JavaScript更加强大、优雅和易于维护。这些特性已经成为现代JavaScript开发的基石,掌握它们对于任何JavaScript开发者都至关重要。本文将深入…...
实现使用RBF(径向基函数)神经网络模拟二阶电机数学模型中的非线性干扰,以及使用WNN(小波神经网络)预测模型中的非线性函数来抵消迟滞影响的功能
下面将详细介绍如何实现使用RBF(径向基函数)神经网络模拟二阶电机数学模型中的非线性干扰,以及使用WNN(小波神经网络)预测模型中的非线性函数来抵消迟滞影响的功能。我们将按照以下步骤进行: 步骤1&#x…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
