Java设计模式:四、行为型模式-05:备忘录模式
文章目录
- 一、定义:备忘录模式
- 二、模拟场景:备忘录模式
- 三、改善代码:备忘录模式
- 3.1 工程结构
- 3.2 备忘录模式模型结构图
- 3.3 备忘录模式定义
- 3.3.1 配置信息类
- 3.3.2 备忘录类
- 3.3.3 记录者类
- 3.3.4 管理员类
- 3.4 单元测试
- 四、总结:备忘录模式
一、定义:备忘录模式

- 备忘录模式:是以可以恢复或者说回滚,配置、版本、悔棋为核心功能的设计模式。
- 这种设计模式属于行为模式。
- 在功能实现上是以不破坏原对象为基础增加备忘录操作类,记录原对象的行为从而实现备忘录模式。
- 备忘录模式的使用场景:后悔药、IDEA 编辑和撤销、小霸王游戏机存档,photoshop 等
二、模拟场景:备忘录模式

- 模拟系统在发布上线的过程中记录线上配置文件用于紧急回滚。
- 在大型互联网公司系统的发布上线一定是易用、安全、可处理紧急状况的,同时为了可以隔离线上和本地环境,一般会把配置文件抽取出来放到线上,避免有人误操作导致本地的配置内容发布出去。
- 同时,线上的配置文件也会在每次变更的时候进行记录,包括:版本号、时间、MD5、内容信息和操作人。
- 在后续上线时如果发现紧急问题,系统就会需要回滚操作,如果执行回滚那么也可以设置配置文件是否回滚。
- 因为每一个版本的系统可能会带着一些配置文件的信息,这个时候就可以很方便的让系统与配置文件一起回滚操作。
- 使用备忘录模式,模拟如何记录配置文件信息。
- 在大型互联网公司系统的发布上线一定是易用、安全、可处理紧急状况的,同时为了可以隔离线上和本地环境,一般会把配置文件抽取出来放到线上,避免有人误操作导致本地的配置内容发布出去。
三、改善代码:备忘录模式
- 备忘录的设计模式实现,重点在于不更改原有类的基础上,增加备忘录类存放记录。
3.1 工程结构
design-step-18
|——src|——main|--java|--com.lino.design|--Admin.java|--ConfigFile.java|--ConfigMemento.java|--ConfigOriginator.java|--test|--java|--com.lino.design.test|--ApiTest.java
3.2 备忘录模式模型结构图

- 以上是工程结构的一个类图,其实相对来说并不复杂,除了原有的配置类
ConfigFile以外,只新增加了三个类。- ConfigMemento:备忘录类,相当于是对原有配置类的扩展。
- ConfigOriginator:记录者类,获取和返回备忘录类对象信息。
- Admin:管理员类,用于操作记录备忘信息,比如你一些列的顺序执行了什么或者某个版本下的内容信息。
3.3 备忘录模式定义
3.3.1 配置信息类
ConfigFile.java
package com.lino.design;import java.util.Date;/*** @description: 配置文件*/
public class ConfigFile {/*** 版本号*/private String versionNo;/*** 内容*/private String content;/*** 时间*/private Date dateTime;/*** 操作人*/private String operator;public ConfigFile(String versionNo, String content, Date dateTime, String operator) {this.versionNo = versionNo;this.content = content;this.dateTime = dateTime;this.operator = operator;}public String getVersionNo() {return versionNo;}public void setVersionNo(String versionNo) {this.versionNo = versionNo;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public Date getDateTime() {return dateTime;}public void setDateTime(Date dateTime) {this.dateTime = dateTime;}public String getOperator() {return operator;}public void setOperator(String operator) {this.operator = operator;}
}
- 配置类可以是任何形式的,这里只是简单的描述一个基本的配置内容信息。
3.3.2 备忘录类
ConfigMemento.java
package com.lino.design;/*** @description: 备忘录*/
public class ConfigMemento {private ConfigFile configFile;public ConfigMemento(ConfigFile configFile) {this.configFile = configFile;}public ConfigFile getConfigFile() {return configFile;}public void setConfigFile(ConfigFile configFile) {this.configFile = configFile;}
}
- 备忘录是对原有配置类的扩展,可以设置和获取配置信息。
3.3.3 记录者类
ConfigOriginator.java
package com.lino.design;/*** @description: 记录者*/
public class ConfigOriginator {private ConfigFile configFile;public ConfigFile getConfigFile() {return configFile;}public void setConfigFile(ConfigFile configFile) {this.configFile = configFile;}public ConfigMemento saveMemento() {return new ConfigMemento(configFile);}public void getMemento(ConfigMemento configMemento) {this.configFile = configMemento.getConfigFile();}
}
- 记录者类除了对 ConfigFile 配置类增加了获取和设置方法外,还增加了 保存
saveMemento()、获取getMemento(ConfigMemento configMemento)。saveMemento():保存备忘录的时候会创建一个备忘录信息,并返回回去,交给管理者处理。getMemento:获取之后并不是直接返回,而是把备忘录的信息交给现在的配置文件this.configFile。
3.3.4 管理员类
Admin.java
package com.lino.design;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @description: 管理员*/
public class Admin {private int cursorIdx = 0;private List<ConfigMemento> mementoList = new ArrayList<>();private Map<String, ConfigMemento> mementoMap = new HashMap<>(16);public void append(ConfigMemento memento) {mementoList.add(memento);mementoMap.put(memento.getConfigFile().getVersionNo(), memento);cursorIdx++;}public ConfigMemento undo() {if (--cursorIdx <= 0) {return mementoList.get(0);}return mementoList.get(cursorIdx);}public ConfigMemento redo() {if (++cursorIdx > mementoList.size()) {return mementoList.get(mementoList.size() - 1);}return mementoList.get(cursorIdx);}public ConfigMemento get(String versionNo) {return mementoMap.get(versionNo);}
}
- 这个类中主要实现的核心功能就是记录配置文件信息,也就是备忘录的效果,之后提供可以回滚和获取的方法,拿到备忘录的具体内容。
- 同时这里设置了两个数据结构来存放备忘录,实际使用中可以按需设置。
List<ConfigMemento>、Map<String, ConfigMemento>。 - 最后是提供的备忘录操作方法。
- 存放
append()、回滚undo()、返回redo()、定向获取get(String versionNo)。
- 存放
3.4 单元测试
ApiTest.java
package com.lino.design.test;import com.alibaba.fastjson.JSON;
import com.lino.design.Admin;
import com.lino.design.ConfigFile;
import com.lino.design.ConfigOriginator;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;/*** @description: 单元测试*/
public class ApiTest {private Logger logger = LoggerFactory.getLogger(ApiTest.class);@Testpublic void test() {Admin admin = new Admin();ConfigOriginator configOriginator = new ConfigOriginator();configOriginator.setConfigFile(new ConfigFile("100001", "配置内容A=哈哈", new Date(), "小零哥"));admin.append(configOriginator.saveMemento());configOriginator.setConfigFile(new ConfigFile("100002", "配置内容A=嘻嘻", new Date(), "小零哥"));admin.append(configOriginator.saveMemento());configOriginator.setConfigFile(new ConfigFile("100003", "配置内容A=默默", new Date(), "小零哥"));admin.append(configOriginator.saveMemento());configOriginator.setConfigFile(new ConfigFile("100004", "配置内容A=嘿嘿", new Date(), "小零哥"));admin.append(configOriginator.saveMemento());// 历史配置(回滚)configOriginator.getMemento(admin.undo());logger.info("历史配置(回滚)undo:{}", JSON.toJSONString(configOriginator.getConfigFile()));// 历史配置(回滚)configOriginator.getMemento(admin.undo());logger.info("历史配置(回滚)undo:{}", JSON.toJSONString(configOriginator.getConfigFile()));// 历史配置(前进)configOriginator.getMemento(admin.redo());logger.info("历史配置(前进)redo:{}", JSON.toJSONString(configOriginator.getConfigFile()));// 历史配置(获取)configOriginator.getMemento(admin.get("100002"));logger.info("历史配置(获取)get:{}", JSON.toJSONString(configOriginator.getConfigFile()));}
}
- 这里包括了四次信息存储和备忘录历史配置操作。
- 通过上面添加了四次配置后,下面分别进行操作是:
回滚1次,再回滚1次,之后向前进1次,最后是获取指定的版本配置。
测试结果
17:04:48.903 [main] INFO com.lino.design.test.ApiTest - 历史配置(回滚)undo:{"content":"配置内容A=嘿嘿","dateTime":1675415088777,"operator":"小零哥","versionNo":"100004"}
17:04:48.913 [main] INFO com.lino.design.test.ApiTest - 历史配置(回滚)undo:{"content":"配置内容A=默默","dateTime":1675415088777,"operator":"小零哥","versionNo":"100003"}
17:04:48.913 [main] INFO com.lino.design.test.ApiTest - 历史配置(前进)redo:{"content":"配置内容A=嘿嘿","dateTime":1675415088777,"operator":"小零哥","versionNo":"100004"}
17:04:48.913 [main] INFO com.lino.design.test.ApiTest - 历史配置(获取)get:{"content":"配置内容A=嘻嘻","dateTime":1675415088777,"operator":"小零哥","versionNo":"100002"}
- 从测试结果效果上可以看到,历史配置按照我们的指令进行了回滚和前进,以及最终通过指定的版本进行获取,符合预期结果。
四、总结:备忘录模式
- 备忘录模式可以满足在不破坏原有属性类的基础上,扩充了备忘录的功能。
- 在以上的实现中我们将配置模拟存放到内存中,如果关机了会导致配置信息丢失,因为在一些真实的场景里还是需要存放到数据中。
- 此种存放到内存中进行回复的场景也不是没有,比如:Photoshop、运营人员操作 ERP 配置活动。也就是即时性的一般不需要存放到库中进行恢复。
- 另外如果是使用内存方式存放备忘录,需要考虑存储问题,避免造成内存大量消耗。
相关文章:
Java设计模式:四、行为型模式-05:备忘录模式
文章目录 一、定义:备忘录模式二、模拟场景:备忘录模式三、改善代码:备忘录模式3.1 工程结构3.2 备忘录模式模型结构图3.3 备忘录模式定义3.3.1 配置信息类3.3.2 备忘录类3.3.3 记录者类3.3.4 管理员类 3.4 单元测试 四、总结:备忘…...
MongoDB实验——MongoDB配置用户的访问控制
MongoDB 配置用户的访问控制 一、 实验原理 理解admin数据库:安装MongoDB时,会自动创建admin数据库,这是一个特殊数据库,提供了普通数据库没有的功能,例如,有些账户角色赋予用户操作多个数据库的权限&…...
golang逃逸技术分析
“ 申请到栈内存好处:函数返回直接释放,不会引起垃圾回收,对性能没有影响。 申请到堆上面的内存才会引起垃圾回收。 func F() { a : make([]int, 0, 20) b : make([]int, 0, 20000) l : 20 c : make([]int, 0, l)} “ a和b代码一样࿰…...
说说你了解的 Nginx
分析&回答 nginx性能数据 高并发连接: 官方称单节点支持5万并发连接数,实际生产环境能够承受2-3万并发。内存消耗少: 在3万并发连接下,开启10个nginx进程仅消耗150M内存 (15M10150M) 1. 正向、反向代理 所谓“代理”,是指在内网边缘 …...
SpringWeb(SpringMVC)
目录 SpringWeb介绍 搭建 SpringWeb SpringWeb介绍 Spring Web是一个基于 Servlet API 构建的原始 web 框架,用于构建基于MVC模式的Web应用程序。在 web 层框架历经 Strust1,WebWork,Strust2 等诸多产品的历代更选 之后,目前业界普…...
Mysql 语句
数据库管理 SQL语言分类 DDL 数据定义语言,用于创建数据库对象,如库、表、索引等 create 创建 create database/table; 数据库/表 create table 表名 (括号内添加类型和字段);drop 删除 drop database/table; 数据库/表…...
软考高级架构师——6、软件架构设计
像学写文章一样,在学会字、词、句之后,就应上升到段落,就应追求文章的“布局谋 篇”,这就是架构。通俗地讲,软件架构设计就是软件系统的“布局谋篇”。 人们在软件工程实践中,逐步认识到了软件架构的重要性…...
虚拟内存相关笔记
虚拟内存是计算机系统内存管理的一个功能,它允许程序认为它们有比实际物理内存更多的可用内存。它使用硬盘来模拟额外的RAM。当物理内存不足时,操作系统将利用磁盘空间作为虚拟内存来存储数据。这种机制提高了资源的利用率并允许更大、更复杂的应用程序的…...
【linux】定时任务讲解
文章目录 一. 在某时刻只执行一次:at1. 设置定时任务2. 查看和删除定时任务 二. 周期性执行任务:cron1. 启动crond进程2. 编辑定时任务3. 查看和删除4. 用户权限4.1. 黑名单4.2指定用户 三. /etc/crontab的管理 一. 在某时刻只执行一次:at 1…...
安卓10创建文件夹失败
最近在做拍照录像功能,已经有了文件读写权限,却发现在9.0手机上正常使用,但是在安卓12系统上根本没有创建文件夹。经过研究发现,创建名称为“DCIM”的文件夹可以,别的又都不行。而且是getExternalStorageDirectory和ge…...
文件操作(c/c++)
文件操作可以概括为几步: 打开文件,写入文件,读取文件,关闭文件 FILE FILE 是一个在C语言中用于文件操作的库函数,它提供了一系列函数来实现文件的创建、打开、读取、写入、关闭等操作。FILE 库函数可以帮助开发者处理…...
设计模式-适配器
文章目录 一、简介二、适配器模式基础1. 适配器模式定义与分类2. 适配器模式的作用与优势3.UML图 三、适配器模式实现方式1. 类适配器模式2. 对象适配器模式3.类适配器模式和对象适配器模式对比 四、适配器模式应用场景1. 继承与接口的适配2. 跨平台适配 五、适配器模式与其他设…...
C. Queries for the Array - 思维
分析: 分析出现矛盾的地方,也就是可能遇到0,并且已有字符串的长度小于等于1,另一种情况就是,遇到了1并且已有字符串不是排好序的,或者遇到了0已有字符串是排好序的,那么可以遍历字符串ÿ…...
音频——硬件拓扑
文章目录 硬件拓扑I2S 数据通路五线模式四线模式两线 TX两线 RX 典型应用硬件连接数据流 硬件拓扑 控制路径:UART/I2C/SPI数据路径:I2S 简略图如下 I2S 数据通路 五线模式 四线模式 两线 TX 两线 RX 典型应用 硬件连接 控制信号:SPI 用…...
Oracle表索引查看方法总结(查看oracle表索引)
Oracle表索引查看方法总结 Oracle是当前应用最广泛的关系数据库,也是多数大型企业使用的数据库。Oracle表索引在提高查询效率方面起着至关重要的作用,掌握该方法也是技术人员必备技能之一。本文总结了几种常见的查看Oracle表索引信息的方法,…...
react css 污染解决方法
上代码 .m-nav-bar {background: #171a21;.content {height: 104px;margin: 0px auto;} }import React from "react"; import styles from ./css.module.scssexport default class NavBar extends React.Component<any, any> {constructor (props: any) {supe…...
volatile 关键字 与 CPU cache line 的效率问题
分析&回答 Cache Line可以简单的理解为CPU Cache中的最小缓存单位。目前主流的CPU Cache的Cache Line大小都是64Bytes。假设我们有一个512字节的一级缓存,那么按照64B的缓存单位大小来算,这个一级缓存所能存放的缓存个数就是512/64 8个。具体参见下…...
又一关键系统上线,理想车云和自动驾驶系统登陆OceanBase
8 月 1 日,理想汽车公布 7 月交付数据,理想汽车 2023 年 7 月共交付新车 34,134 辆,同比增长 227.5%,并已连续两个月交付量突破三万。至此,理想汽车 2023 年累计交付量已经达到 173,251 辆,远超 2022 年全年…...
SIEM(安全信息和事件管理)解决方案
什么是SIEM 安全信息和事件管理(SIEM)是一种可帮助组织在安全威胁危害到业务运营之前检测、分析和响应安全威胁的解决方案,将安全信息管理 (SIM) 和安全事件管理 (SEM) 结合到一个安全管理系统中。SIEM 技术从广泛来源收集事件日志数据&…...
Go 自学:map关联数组
以下代码展示了如何建立一个map。 我们可以使用delete删除map中的元素。 我们还可以使用loop遍历map中的所有元素。 package mainimport ("fmt" )func main() {languages : make(map[string]string)languages["JS"] "Javascript"languages[&qu…...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
