Qt C++设计模式->备忘录模式

备忘录模式(Memento Pattern)是一种行为型设计模式,用于在不破坏封装性的前提下,捕获并保存对象的内部状态,以便在将来的某个时刻可以恢复到之前的状态。备忘录模式的核心是状态的保存和恢复,常用于实现撤销、回滚等功能。

备忘录模式的应用场景

备忘录模式特别适合以下场景:

  1. 撤销/恢复操作:例如文本编辑器中的撤销功能,通过备忘录保存每次操作的状态,用户可以随时回到某个历史状态。

  2. 数据快照:保存对象在某个时刻的快照,以便之后回溯或调试。

  3. 事务管理:在处理复杂的事务时,可以在中间点保存状态,当某个操作失败时,回滚到之前的状态。

备忘录模式的核心

备忘录模式的主要组成部分包括:

  1. 发起者(Originator):负责创建并恢复备忘录,保存当前的状态到备忘录中,或者从备忘录中恢复状态。

  2. 备忘录(Memento):用于存储发起者的内部状态,不对外公开备忘录的实现细节。

  3. 负责人(Caretaker):负责保存和管理备忘录,但不会操作或修改备忘录的内容。它只知道备忘录保存的状态,并在需要时将备忘录传递回发起者进行状态恢复。

备忘录模式强调的是封装性,发起者的内部状态不应该对外暴露,备忘录类也应该避免暴露这些细节。

备忘录模式的示例代码

假设我们在开发一个文本编辑器,并希望提供撤销和恢复功能,每当用户输入一段文本时,我们将保存当前状态,以便用户可以随时撤销操作。

1. 定义发起者、备忘录和负责人

#include <QDebug>
#include <QString>
#include <QStack>// 备忘录类:保存文本编辑器的状态
class Memento {
private:QString state;  // 保存的状态public:Memento(const QString& state) : state(state) {}QString getState() const {return state;  // 返回保存的状态}
};// 发起者类:文本编辑器
class TextEditor {
private:QString text;  // 当前的文本状态public:void setText(const QString& newText) {text = newText;}QString getText() const {return text;}// 创建备忘录,保存当前状态Memento* save() const {return new Memento(text);}// 从备忘录中恢复状态void restore(Memento* memento) {if (memento) {text = memento->getState();}}
};// 负责人类:管理备忘录
class Caretaker {
private:QStack<Memento*> history;  // 保存备忘录的栈public:void saveMemento(Memento* memento) {history.push(memento);  // 保存当前状态的备忘录}Memento* undo() {if (!history.isEmpty()) {Memento* lastState = history.pop();  // 取出最后一个保存的备忘录return lastState;}return nullptr;  // 没有更多历史状态}~Caretaker() {// 清理保存的备忘录while (!history.isEmpty()) {delete history.pop();}}
};// 使用示例
int main() {TextEditor* editor = new TextEditor();Caretaker* caretaker = new Caretaker();// 初始文本editor->setText("Hello");qDebug() << "Current text:" << editor->getText();  // 输出:Current text: Hello// 保存状态caretaker->saveMemento(editor->save());// 用户修改文本editor->setText("Hello, World");qDebug() << "Current text after modification:" << editor->getText();  // 输出:Current text after modification: Hello, World// 再次保存状态caretaker->saveMemento(editor->save());// 用户再次修改文本editor->setText("Hello, Qt!");qDebug() << "Current text after second modification:" << editor->getText();  // 输出:Current text after second modification: Hello, Qt!// 执行撤销操作editor->restore(caretaker->undo());qDebug() << "Current text after undo:" << editor->getText();  // 输出:Current text after undo: Hello, World// 再次执行撤销操作editor->restore(caretaker->undo());qDebug() << "Current text after second undo:" << editor->getText();  // 输出:Current text after second undo: Hello// 清理内存delete editor;delete caretaker;return 0;
}

代码解析

  • Memento类:这是备忘录类,负责保存发起者的状态。在这个例子中,它保存文本编辑器中的文本状态,并通过getState方法提供对状态的访问。

  • TextEditor类:这是发起者类,它拥有当前的文本状态,并且可以创建备忘录来保存当前状态或从备忘录中恢复状态。

  • Caretaker类:这是负责人类,它保存所有的备忘录(通过栈存储历史状态),并在需要时将备忘录返回给发起者进行状态恢复。undo方法从栈中弹出最后保存的状态,模拟撤销操作。

  • 客户端代码:客户端通过修改文本,并在每次修改后保存状态。通过调用Caretakerundo方法,客户端可以恢复到之前的文本状态,模拟撤销操作。

备忘录模式的优点

  • 保存历史状态:备忘录模式允许你保存对象的状态,并在将来恢复这些状态。非常适合实现撤销、恢复、回滚等功能。

  • 封装性好:备忘录类不暴露发起者的内部状态,保证了发起者的封装性。发起者和负责人只通过备忘录进行状态的保存和恢复,而不需要直接操作发起者的状态。

  • 减少耦合:负责人只负责保存和管理备忘录,而不直接参与发起者的逻辑,职责清晰。

备忘录模式的缺点

  • 内存开销大:每次保存对象的状态都需要创建一个新的备忘录对象,尤其是当对象状态非常庞大时,可能会导致大量的内存占用。

  • 实现复杂性:如果对象的状态非常复杂,备忘录模式的实现也会相应复杂,尤其是在需要保存多个部分或大对象时。

适合使用备忘录模式的情况

  • 需要实现撤销/恢复操作:例如文本编辑器、绘图工具、IDE等支持撤销/恢复功能的应用程序。

  • 需要保存对象的历史状态:当系统需要定期保存某些对象的状态以便将来回溯时,可以使用备忘录模式。

  • 需要避免直接暴露内部状态:如果需要在多个地方保存对象的状态,但不想让外界直接访问或修改对象的内部状态,备忘录模式是一个很好的选择。

不适合使用备忘录模式的情况

  • 对象状态非常庞大:如果发起者的状态非常庞大,频繁创建备忘录会带来较大的内存开销,不适合使用备忘录模式。

  • 状态变化频繁:如果对象状态变化频繁,并且每次都需要保存,那么备忘录模式会带来大量性能问题。

Qt中的备忘录模式应用

在Qt开发中,备忘录模式可以用于实现撤销/恢复功能。例如,在一个文本编辑器或绘图工具中,用户的每次操作都可能改变对象的状态,这些操作可以通过备忘录模式保存下来,并在需要时回滚或恢复。Qt中有些类(如QUndoStack)可以直接实现类似的撤销功能,它们内部也可能应用了备忘录模式的思想。

总结

备忘录模式通过保存对象的状态并在将来进行恢复,使得系统能够实现撤销、回滚等功能,同时保证了对象内部状态的封装性。它非常适合用于保存对象的历史状态、支持撤销操作的场景。然而,备忘录模式的内存开销较大,不适合频繁状态变化且状态庞大的对象。

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

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

相关文章

IP地址类型选择指南:动态IP、静态IP还是数据中心IP?

你是否曾经困惑于如何选择最适合业务需求的IP地址类型&#xff1f;面对动态IP、静态IP和数据中心IP这三种选择&#xff0c;你是否了解它们各自对你的跨境在线业务可能产生的深远影响&#xff1f; 在跨境电商领域&#xff0c;选择合适的IP类型对于业务的成功至关重要。动态IP、…

gitee开源商城diygw-mall

DIYGW可视化开源商城系统。所的界面布局显示都通过低代码可视化开发工具生成源码实现。支持集成微信小程序支付。 DIYGW可视化开源商城系统是一款基于thinkphp8 framework、 element plus admin、uniapp开发而成的前后端分离系统。 开源商城项目源码地址&#xff1a;diygw商城…

Java中String类的常见操作Api

目录 String类的常见操作 1).int indexOf (char 字符) 2).int lastIndexOf(char 字符) 3).int indexOf(String 字符串) 4).int lastIndexOf(String 字符串) 5).char charAt(int 索引) 6).Boolean endWith(String 字符串) 7).int length() 8).boolean equals(T 比较对象) 9).b…

区块链积分系统:重塑支付安全与商业创新的未来

在当今社会&#xff0c;数字化浪潮席卷全球&#xff0c;支付安全与风险管理议题日益凸显。随着交易频次与规模的不断扩大&#xff0c;传统支付体系正面临前所未有的效率、合规性和安全挑战。 区块链技术&#xff0c;凭借其去中心化、高透明度以及数据不可篡改的特性&#xff0c…

SSH 公钥认证:从gitlab clone项目repo到本地

这篇文章的分割线以下文字内容由 ChatGPT 生成&#xff08;我稍微做了一些文字上的调整和截图的补充&#xff09;&#xff0c;我review并实践后觉得内容没有什么问题&#xff0c;由此和大家分享。 假如你想通过 git clone git10.12.5.19:your_project.git 命令将 git 服务器上…

简单的maven nexus私服学习

简单的maven nexus私服学习 1.需求 我们现在使用的maven私服是之前同事搭建的&#xff0c;是在公司的一台windows电脑上面&#xff0c;如果出问题会比较难搞&#xff0c;所以现在想将私服迁移到我们公司的测试服务器上&#xff0c;此处简单了解一下私服的一些配置记录一下&am…

多线程(二):Thread类常见的属性和方法

目录 1、run & start 2、Thread类常见的属性和方法 2.1 构造方法 2.2 属性 3、后台进程 & 前台进程 4、setDaemon 5、isAlive 6、终止一个线程 6.1 变量捕获 6.2 currentThread & isInterrupted & interrupt 1、run & start 在多线程&#xff08…

Java面试宝典-Java集合01

Java面试宝典-Java集合01 目录 Java面试宝典-Java集合01 1、Java中常用的集合有哪些&#xff1f; 2、Collection 和 Collections 有什么区别&#xff1f; 3、为什么集合类没有实现 Cloneable 和 Serializable 接口&#xff1f; 4、数组和集合有什么本质区别&#xff1f; 5、数组…

Java | Leetcode Java题解之第470题用Rand7()实现Rand10()

题目&#xff1a; 题解&#xff1a; class Solution extends SolBase {public int rand10() {int a, b, idx;while (true) {a rand7();b rand7();idx b (a - 1) * 7;if (idx < 40) {return 1 (idx - 1) % 10;}a idx - 40;b rand7();// get uniform dist from 1 - 63…

蓝桥杯【物联网】零基础到国奖之路:十七. 扩展模块之单路ADC和NE555

蓝桥杯【物联网】零基础到国奖之路:十七. 扩展模块之单路ADC和NE555 第一节 硬件解读第二节 CubeMx配置第三节 代码1&#xff0c;脉冲部分代码2&#xff0c;ADC部分代码![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/57531a4ee76d46daa227ae0a52993191.png) 第一节 …

React技术在Meta Connect 2024大会

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

熵权法计算评价指标权重——使用Excel VBA实现

[ 熵权法 ] 信息是系统有序程度的一个度量&#xff0c;熵是系统无序程度的一个度量&#xff1b;根据信息熵的定义&#xff0c;对于某项指标&#xff0c;可以用熵值来判断某个指标的离散程度&#xff0c;其信息熵值越小&#xff0c;指标的离散程度越大&#xff0c; 该指标对综合…

数据库——表格之间的关系(表格之间的连接和处理)

数据库表格之间经常存在各种关系&#xff1a; 一对一、一对多、多对多 1.一对一 —— 丈夫表&#xff0c;妻子表为例 连接方式一&#xff1a;合并为一张表 这种方式对于一对一来说最优 连接方式二&#xff1a;在其中一张表内加入一个外键&#xff0c;连接另一张表 连…

ARM base instruction -- sdiv

有符号除法运算 Signed Divide divides a signed integer register value by another signed integer register value, and writes the result to the destination register. The condition flags are not affected. 将一个有符号整数寄存器值除以另一个有符号整数寄存器值&am…

Java中的switch分支结构

switch分支结构 switch分支结构1.基本语法2.说明3.流程图4.案例5.注意事项6.练习7.switch和if的比较 switch分支结构 1.基本语法 switch&#xff08;表达式&#xff09;{case 常量1: //当...语句块1;break;case 常量2: 语句块2;break;...case 常量n: 语句块n;break;defaul…

路径跟踪之导航向量场——二维导航向量场

今天带来一期轨迹跟踪算法的讲解&#xff0c;首先讲解二维平面中的导航向量场[1]。该方法具有轻量化、计算简便、收敛性强等多项优点。该方法根据期望的轨迹函数&#xff0c;计算全局位置的期望飞行向量&#xff0c;将期望飞行向量转为偏光角&#xff0c;输入底层控制器&#x…

prometheus client_java实现进程的CPU、内存、IO、流量的可观测

文章目录 1、获取进程信息的方法1.1、通过读取/proc目录获取进程相关信息1.2、通过Linux命令获取进程信息1.2.1、top&#xff08;CPU/内存&#xff09;命令1.2.2、iotop&#xff08;磁盘IO&#xff09;命令1.2.3、nethogs&#xff08;流量&#xff09;命令 2、使用prometheus c…

AAA Mysql与redis的主从复制原理

一 &#xff1a;Mysql主从复制 重要的两个日志文件&#xff1a;bin log 和 relay log bin log&#xff1a;二进制日志&#xff08;binnary log&#xff09;以事件形式记录了对MySQL数据库执行更改的所有操作。 relay log&#xff1a;用来保存从节点I/O线程接受的bin log日志…

用凡尔码系统进行隐患排查二维码的制作

隐患排查是企业安全管理的重要环节&#xff0c;通过定期或不定期地对生产设备、作业场所、作业人员等进行检查&#xff0c;发现并消除安全隐患&#xff0c;预防事故的发生。隐患排查的效率和质量直接影响到企业的安全生产水平和经济效益。 传统的隐患排查方法主要依靠纸质进行…

PostgreSQL学习笔记七:常规SQL操作

PostgreSQL 支持标准的 SQL 语句&#xff0c;同时也扩展了一些特有的功能。以下是一些常规的 SQL 语句示例&#xff0c;这些示例涵盖了数据定义、数据操作和数据查询的基本操作&#xff1a; 数据定义语言 (DDL 创建数据库&#xff1a; CREATE DATABASE mydatabase;创建表&#…