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…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...

论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing
Muffin 论文 现有方法 CRADLE 和 LEMON,依赖模型推理阶段输出进行差分测试,但在训练阶段是不可行的,因为训练阶段直到最后才有固定输出,中间过程是不断变化的。API 库覆盖低,因为各个 API 都是在各种具体场景下使用。…...
Python竞赛环境搭建全攻略
Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型(算法、数据分析、机器学习等)不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...

高效的后台管理系统——可进行二次开发
随着互联网技术的迅猛发展,企业的数字化管理变得愈加重要。后台管理系统作为数据存储与业务管理的核心,成为了现代企业不可或缺的一部分。今天我们要介绍的是一款名为 若依后台管理框架 的系统,它不仅支持跨平台应用,还能提供丰富…...
Java中栈的多种实现类详解
Java中栈的多种实现类详解:Stack、LinkedList与ArrayDeque全方位对比 前言一、Stack类——Java最早的栈实现1.1 Stack类简介1.2 常用方法1.3 优缺点分析 二、LinkedList类——灵活的双端链表2.1 LinkedList类简介2.2 常用方法2.3 优缺点分析 三、ArrayDeque类——高…...