【设计模式-4.7】行为型——备忘录模式

article/2025/6/7 23:04:57

说明:本文介绍行为型设计模式之一的备忘录模式

定义

备忘录模式(Memento Pattern)又叫作快照模式(Snapshot Pattern)或令牌模式(Token Pattern)指在不破坏封装的前提下,捕获一个对象的内部状态,并在对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态,属于行为型设计模式。

(引自《设计模式就该这样学》P348,发现作者很喜欢使用”XX模式又叫作XX模式“这样的表述,苦笑)

编辑器

假设开发一款编辑器软件,如下,有载入文档、追加内容、清空内容功能;

(文档类,Doc)

/*** 文档类*/
public class Doc {/*** 文档标题*/private String title;/*** 文档内容*/private StringBuffer body;public Doc(String title) {this.title = title;this.body = new StringBuffer();}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public StringBuffer getBody() {return body;}public void setBody(StringBuffer body) {this.body = body;}
}

(编辑器,Editor)

/*** 编辑器类*/
public class Editor {private Doc doc;/*** 载入文档*/public Editor(Doc doc) {System.out.println(">>>载入文档");this.doc = doc;show();}/*** 追加内容*/public void append(String content){System.out.println(">>>追加内容");doc.getBody().append(content);show();}/*** 清空内容*/public void clear() {System.out.println(">>>清空文档");doc.getBody().setLength(0);show();}/*** 保存内容*/public void save() {System.out.println(">>>保存中");// todoSystem.out.println(">>>保存成功");}/*** 展示内容*/public void show() {System.out.println(">>>展示内容");System.out.println("文档标题:" + doc.getTitle());System.out.println("文档内容:" + doc.getBody());}
}

(客户端使用,Client)

public class Client {public static void main(String[] args) {// 打开编辑器,开始写作Editor myDoc = new Editor(new Doc("《论程序员的自我修养》"));// 巴拉巴拉,写作中myDoc.append("\n第一章:程序员必备知识");myDoc.append("\n第二章:论艺术涵养对程序员编码的影响");// 看看,嗯,写得很好myDoc.show();// 保存myDoc.save();// 继续myDoc.append("\n第三章:论打游戏技术与程序员技术之间的关联");// 误操作。。。myDoc.clear();}
}

可见,如果误操作导致文档内容被清空,九分甚至十分的糟糕

在这里插入图片描述

针对以上功能,利用备忘录模式进行改造,如下:

(首先,创建一个历史记录对象,保存文档内容,History)

/*** 历史记录类*/
public class History {private StringBuffer body;public History(StringBuffer body) {this.body = body;}public StringBuffer getBody() {return body;}
}

(其次,文档对象中,增加保存历史记录,恢复历史记录的方法)

/*** 文档类*/
public class Doc {/*** 文档标题*/private String title;/*** 文档内容*/private StringBuffer body;public Doc(String title) {this.title = title;this.body = new StringBuffer();}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public StringBuffer getBody() {return body;}public void setBody(StringBuffer body) {this.body = body;}/*** 创建历史记录*/public History createHistory() {return new History(new StringBuffer(body));}/*** 恢复历史记录*/public void restoreHistory(History history) {body = history.getBody();}
}

注意创建历史记录方法里,使用了new StringBuffer(body),而不是直接传递body,这里涉及到深拷贝、浅拷贝的问题,大家可以试试看有什么区别。

(最后,改造编辑器类,增加撤销上一步操作的方法,并在每次修改操作后增加创建快照操作)

import java.util.List;/*** 编辑器类*/
public class Editor {/*** 文档对象*/private Doc doc;/*** 历史记录集合*/private List<History> historyList;/*** 历史记录版本号* 初始值为-1*/private int historyVersion = -1;/*** 载入文档*/public Editor(Doc doc) {System.out.println(">>>载入文档");this.doc = doc;historyList = new java.util.ArrayList<>();backup();show();}/*** 追加内容*/public void append(String content){System.out.println(">>>追加内容");doc.getBody().append(content);backup();show();}/*** 清空内容*/public void clear() {System.out.println(">>>清空文档");doc.getBody().setLength(0);backup();show();}/*** 保存内容*/public void save() {System.out.println(">>>保存中");// todoSystem.out.println(">>>保存成功");}/*** 展示内容*/public void show() {System.out.println(">>>展示内容");System.out.println("文档标题:" + doc.getTitle());System.out.println("文档内容:" + doc.getBody());}/*** 保存历史记录* 或者说创建快照*/private void backup() {historyList.add(doc.createHistory());historyVersion++;}/*** 撤回上一步*/public void undo() {System.out.println(">>>撤销操作");if (historyVersion == 0) {return;}historyVersion--;History history = historyList.get(historyVersion);doc.restoreHistory(history);show();}
}

(客户端使用,Client)

public class Client {public static void main(String[] args) {// 打开编辑器,开始写作Editor myDoc = new Editor(new Doc("《论程序员的自我修养》"));// 巴拉巴拉,写作中myDoc.append("\n第一章:程序员必备知识");myDoc.append("\n第二章:论艺术涵养对程序员编码的影响");// 看看,嗯,写得很好myDoc.show();// 保存myDoc.save();// 继续myDoc.append("\n第三章:论打游戏技术与程序员技术之间的关联");// 误操作。。。myDoc.clear();// 撤回上一步myDoc.undo();}
}

可见撤销操作成功恢复内容

在这里插入图片描述

多次撤销,可实现逐步回退操作

        // 撤回上一步myDoc.undo();myDoc.undo();myDoc.undo();myDoc.undo();

如下,非常nice。如果需要开发往后撤退的功能,也完全可以。

在这里插入图片描述

使用场景

在《设计模式就该这样学》(P365)这本书中,提到状态模式适用于以下场景:

(1)需要保存历史快照的场景。

(2)希望在对象之外保存状态,且除了自己,其他类对象无法访问状态保存的具体内容。

我觉得如果项目中,需要保存历史记录的场景,可以考虑使用备忘录模式进行改造。

总结

本文介绍了行为型设计模式中的状态模式,参考《设计模式就该这样学》、《秒懂设计模式》两书,编辑器场景是《秒懂设计模式》中的举例。


http://www.hkcw.cn/article/MdGUwaBFLh.shtml

相关文章

ADRV9026 JESD204C测试

ADRV9026在650MHz到6GHz的宽频率范围内工作。 接收器通道支持高达200MHz的带宽&#xff0c; 数据传输速率高达24.33 Gbps&#xff0c; 可跨&#xff08;最多&#xff09; 四个JESD204B/JESD204C通道传输。 发射机信道在与接收机相同的频率范围内工作。 每个发射机信道支持高达4…

压缩格式未知或数据损坏:如何应对压缩包无法打开的情况

压缩包在日常工作和生活中非常常见&#xff0c;用于节省空间并方便文件传输。然而&#xff0c;用户在尝试解压文件时&#xff0c;有时会遇到诸如“压缩格式未知”或“数据已损坏”等错误信息。 这种情况可能导致文件无法打开&#xff0c;影响工作效率甚至造成数据丢失。本文将…

升级:用vue canvas画一个能源监测设备和设备的关系监测图!

用vue canvas画一个能源电表和设备的监测图-CSDN博客 上一篇文章&#xff0c;我是用后端的数据来画出监测图。这次我觉的&#xff0c;用前端来控制数据&#xff0c;更爽。 本期实现功能&#xff1a; 1&#xff0c;得到监测设备和设备的数据&#xff0c;然后进行存库 2&…

《数据挖掘》- 房价数据分析

这里写目录标题 采用的技术1. Python编程语言2. 网络爬虫库技术点对比与区别项目技术栈的协同工作流程 代码解析1. 导入头文件2. 读取原始数据3. 清洗数据4. 数据分割4.1 统计房屋信息的分段数量4.2 将房屋信息拆分为独立列4.3 处理面积字段4.4 删除原始房屋信息列 5. 可视化分…

Axure-元件流程图

Axure-02 线框图元件使用 目标 元件基本介绍 基础元件的使用 表单型元件的使用 菜单与表格元件的使用 案例&#xff1a;个人简历表 元件基本介绍 概述 在Axure RP中&#xff0c;元件是构建原型图的基础模块。 将元件从元件库里拖拽到画布中&#xff0c;即可添加元件到你…

深入解析ReactJS中JSX的底层工作原理

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storms…

《Pytorch深度学习实践》ch5-Logistic回归

------B站《刘二大人》 1.Classification 经典的分类数据集&#xff1a;MNIST&#xff08;0 - 9&#xff09; 导入数据集&#xff1a;&#xff08;路径&#xff0c;训练集/测试集&#xff0c;是否下载&#xff09; import torchvision train_set torchvision.datasets.MINI…

HCIP(BGP综合实验)

一、实验拓扑 AS 划分&#xff1a; AS1&#xff1a;R1&#xff08;环回 L0:172.16.0.1/32&#xff0c;L1:192.168.1.0/24&#xff09;AS2&#xff1a;R2、R3、R4、R5、R6、R7&#xff08;内部运行 OSPF&#xff0c;AS 号为 64512 和 64513 的联盟&#xff09;AS3&#xff1a;R…

AI数字人技术革新进行时:井云数字人如何重塑人机交互未来?

老板们注意了&#xff01;不用反复真人出镜拍摄&#xff0c;AI数字人来帮你做口播&#xff0c;只需3分钟克隆你的形象和声音&#xff0c;输入文案24小时随时都能生成视频&#xff01; 在元宇宙概念持续升温、虚拟与现实加速融合的当下&#xff0c;AI数字人正以惊人的速度从科幻…

使用n8n工作流编排AI Agent处理爬取的网页信息

目录 使用docker本地部署n8n 例子 1.点击触发节点 2.RSS Read节点 3.Limit节点 4.FireCrawl节点 5.AI Agent节点 6.Aggregate节点 7.Markdown节点 8.Send Email节点 使用docker本地部署n8n docker-compose.yml&#xff1a; version: "3.8" services:n8n:…

【软考】计算机系统构成及硬件基础知识

文章目录 进制码制逻辑运算CPU的构成、CPU性能指标多级存储、存储器分类、高速缓存Cache总线指令、寻址方式、RISC/CISC流水线的概念、周期、计算公式输入输出系统校验码 (奇偶校验、CRC、海明码) 进制 二进制&#xff1a;0、1 八进制&#xff1a;0、1、2、3、4、5、6、7 十进…

纯色图片生成器

就是在测试代码的时候有时候会用到一些纯色的图片进行测试&#xff0c;后面突发奇想才搞了一个纯色生成器玩玩&#xff0c;有需要的可以三连自取哦~ 一键生成高清纯色背景&#xff0c;满足设计、演示、测试需求。支持自定义颜色代码&#xff0c;快速导出PNG。 效果如下&#x…

【知识点】第4章:程序控制结构

文章目录 知识点整理程序控制结构 练习题判断题简答题选择题 知识点整理 程序控制结构 程序由3种基本结构组成&#xff1a;顺序结构、分支结构和循环结构。 顺序结构是程序按照顺序依次执行的一种运行方式。 分支结构是程序根据条件判断结果而选择不同向前执行路径的一种方式…

DeepSeek眼中的文明印记:山海经

一、山海经到底是怎么回事&#xff1f; 《山海经》是中国古代一部极具神秘色彩的文化典籍&#xff0c;成书时间跨度较大&#xff08;大致从战国至汉代&#xff09;&#xff0c;内容庞杂&#xff0c;涉及神话、地理、物产、巫术、医学、民俗等多个领域。关于它的性质&#xff0…

win11中使用grep命令

Download grep-2.5.4-setup.exe (GnuWin) 安装后&#xff0c;在控制面板中设置好环境变量 Path中加入&#xff1a; E:\software\GnuWin32\bin 测试&#xff1a;

java30

1.多线程 多线程实现方式&#xff1a; 继承Thread: 注意&#xff1a;使用的是start方法&#xff0c;而不是run方法 实现runnable: 利用Callable接口和Future接口方式实现&#xff1a; 总结&#xff1a; 线程类Thread的成员方法&#xff1a; 注意&#xff1a;父类没有throws异常…

【复习】软件测试

软件测试复习 试题分布 软件测试绪论 软件测试定义 软件测试分类&#xff1a; 软件测试目的&#xff1a; 测试与开发的关系 过程和流程&#xff1a; 过程和流程是对同一事物不同级别的描述&#xff0c;并不是包含关系。过程抓大放小&#xff0c;流程事无巨细。白盒测…

闲谈PMIC和SBC

今天不卷&#xff0c;简单写点。 在ECU设计里&#xff0c;供电芯片选型是逃不开的话题&#xff0c;所以聊聊PMIC或者SBC的各自特点&#xff0c;小小总结下。 PMIC&#xff0c;全称Power Management Intergrated Circuits&#xff0c;听名字就很专业&#xff1a;电源管理&…

风机那么高,叶片怎么检查得到?

也许你曾见过电力巡检员爬到电杆上作检修工作&#xff0c;几层楼的高度尚且让人感觉危险值飙升&#xff0c;那巨人般高大的风机呢&#xff1f; 也许一想就让人脚底发颤。 在那些离我们普通人遥远的山上、海上&#xff0c;有这么一个群体&#xff0c;他们要在灼灼烈日下爬上14…

mac下通过anaconda安装Python

本次分享mac下通过anaconda安装Python、Jupyter Notebook、R。 anaconda安装 点击&#x1f449;https://www.anaconda.com/download&#xff0c; 点击Mac系统安装包&#xff0c; 选择Mac芯片&#xff1a;苹果芯片 or intel芯片&#xff0c; 选择苹果芯片图形界面安装&#x…