设计模式行为型——备忘录模式

目录

什么是备忘录模式

备忘录模式的实现

备忘录模式角色

备忘录模式类图

备忘录模式举例

备忘录模式代码实现

备忘录模式的特点

优点

缺点

使用场景

注意事项

实际应用


什么是备忘录模式

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

        备忘录模式提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原,当前很多软件都提供了撤销操作,其中就使用了备忘录模式。

备忘录模式的实现

备忘录模式角色

  1. 原发器角色(Originator):负责创建一个备忘录,记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。同时原发器还可以根据需要决定Memento存储Originator的那些内部状态。
  2. 备忘录角色(Memento):负责存储Originator对象的内部状态,并可以防止Originator以外的其他对象访问备忘录。备忘录有两个接口:Caretaker只能看到备忘录的窄接口,他只能将备忘录传递给其他对象。Originator却可看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。
  3. 管理者角色(Caretaker):管理者又称为负责人,它负责保存备忘录。在管理者类中可以存储一个或多个备忘录对象,它只负责存储对象,而不能修改对象(负责任类只提供备忘录对象的读写接口,不提供备忘录属性的读写接口),也无须知道对象的实现细节。管理者对象可以保存一个备忘录数组,从而实现原发器的多次撤销。其存储这些状态信息,除了原发器外其他对象都是不可以访问的,否则就违反了封装的原则。

        所以为了实现备忘录模式的封装,需要对备忘录的访问做些控制:

        对原发器:可以访问备忘录里的所有信息。
        对管理者:不可以访问备忘录里面的数据,但是他可以保存备忘录并且可以将备忘录传递给其他对象。
        其他对象:不可访问也不可以保存,它只负责接收从负责人那里传递过来的备忘录同时恢复原发器的状态。

        所以就备忘录模式而言理想的情况就是只允许生成该备忘录的那个原发器访问备忘录的内部状态。

备忘录模式类图

备忘录模式举例

        要开发一个简单的草稿箱功能,假设只保留文档标题和正文,那么文档标题和正文就属于原发器角色,还需要创建一个文章备忘录,和一个草稿箱作为管理者角色,保存历史文章信息。

        文章备忘录 包含了要被恢复的对象的状态。编辑器 创建并在 文章备忘录 对象中存储状态。草稿箱 对象负责从 文章备忘录 中恢复对象的状态。

备忘录模式代码实现

原发器角色

package com.common.demo.pattern.memento;/*** @author Evan Walker 昂焱数据: https://www.ayshuju.com* @version 1.0* @desc 原发器角色 - 编辑器* @date 2023/08/05 10:26:52*/
public class Editor {private String title;private String content;public Editor() {}public Editor(String title, String content) {this.title = title;this.content = content;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public ArticleMemento saveToMemento(){ArticleMemento articleMemento = new ArticleMemento(this.title, this.content);return articleMemento;}public void undoFromMemento(ArticleMemento articleMemento){this.title = articleMemento.getTitle();this.content = articleMemento.getContent();}@Overridepublic String toString() {return "Editor{" +"title='" + title + '\'' +", content='" + content + '\'' +'}';}
}

备忘录角色

package com.common.demo.pattern.memento;/*** @author Evan Walker 昂焱数据: https://www.ayshuju.com* @version 1.0* @desc 备忘录角色 - 文章* @date 2023/08/05 10:28:21*/
public class ArticleMemento {// 标题private String title;// 正文private String content;public ArticleMemento() {}public ArticleMemento(String title, String content) {this.title = title;this.content = content;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}
}

管理者角色

package com.common.demo.pattern.memento;import java.util.Stack;/*** @author Evan Walker 昂焱数据: https://www.ayshuju.com* @version 1.0* @desc 管理员角色  - 草稿箱* @date 2023/08/05 10:30:41*/
public class DraftsBox {// 存储 - 后进先出private final Stack<ArticleMemento> STACK = new Stack<>();public ArticleMemento getMemento() {ArticleMemento articleMemento = STACK.pop();return articleMemento;}public void addMemento(ArticleMemento articleMemento) {STACK.push(articleMemento);}
}

测试类

package com.common.demo.pattern.memento;/*** @author Evan Walker 昂焱数据: https://www.ayshuju.com* @version 1.0* @desc 测试类* @date 2023/08/05 10:31:12*/
public class Test {public static void main(String[] args) {DraftsBox draftsBox = new DraftsBox();Editor editor = new Editor("初始化", "首次发表");ArticleMemento articleMemento = editor.saveToMemento();draftsBox.addMemento(articleMemento);System.out.println("--------第一次编辑,保存草稿之后,信息如下:---------");System.out.println(editor);editor.setTitle("第一次修改");editor.setContent("首次发表后,第一次修改");ArticleMemento articleMemento1 = editor.saveToMemento();draftsBox.addMemento(articleMemento1);System.out.println("--------第一次修改,保存草稿之后,信息如下:---------");System.out.println(editor);editor.setTitle("第二次修改");editor.setContent("首次发表后,第二次修改");System.out.println("--------第一次修改,还没保存草稿,信息如下:---------");System.out.println(editor);System.out.println("-------撤销操作---------");ArticleMemento memento = draftsBox.getMemento();editor.undoFromMemento(memento);System.out.println("--------第一次撤销,之后,信息如下:---------");System.out.println(editor);ArticleMemento memento2 = draftsBox.getMemento();editor.undoFromMemento(memento2);System.out.println("--------第二次撤销,之后,信息如下:---------");System.out.println(editor);}}

测试截图 

备忘录模式的特点

优点

  1. 提供了对象状态的存储与恢复机制:备忘录模式可以将对象的内部状态保存在备忘录中,当需要时能够比较方便地将数据恢复到某个历史的状态。
  2. 实现了内部状态的封装:除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
  3. 支持多次撤销和恢复操作:备忘录模式可以保存多个状态快照,使得客户端可以根据需求选择恢复到某个特定的状态。

缺点

  1. 资源消耗大:如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。
  2. 对象状态的存储和恢复需要时间和计算资源:备忘录模式需要对对象状态进行序列化和反序列化操作,可能需要消耗一定的时间和计算资源。

使用场景

  1. 需要在不破坏对象封装性的前提下保存和恢复对象的状态。
  2. 需要实现多级撤销和恢复操作。
  3. 需要保存对象历史状态以供分析和审计。

注意事项

  1. 注意备忘录对象的生命周期管理,避免资源泄露和内存溢出。
  2. 确保备忘录对象只被相关的原发器对象访问,避免违反封装原则。
  3. 为了节约内存,可使用原型模式+备忘录模式。

实际应用

  1. 文本编辑器:可以使用备忘录模式来实现文本编辑器的撤销和恢复功能,将文本的不同版本保存在备忘录中。
  2. 游戏进度保存与加载:游戏中可以使用备忘录模式来保存和加载游戏的进度,使得玩家可以随时恢复到之前的某个状态。
  3. 软件撤销和恢复功能:一些软件工具或者编辑器可以使用备忘录模式来实现撤销和恢复功能,保存用户对文件的修改历史。

更多消息资讯,请访问昂焱数据(https://www.ayshuju.com)

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

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

相关文章

高并发负载均衡---LVS

目录 前言 一&#xff1a;负载均衡概述 二&#xff1a;为啥负载均衡服务器这么快呢&#xff1f; ​编辑 2.1 七层应用程序慢的原因 2.2 四层负载均衡器LVS快的原因 三&#xff1a;LVS负载均衡器的三种模式 3.1 NAT模式 3.1.1 什么是NAT模式 3.1.2 NAT模式实现LVS的缺点…

openwr折腾记7-Frpc使用自主域名解析透传本地服务免费不断线的探索

Frpc使用自主域名解析透传本地服务 综述frp透传http服务结构流程 第一部分openwrt-frpc客户端配置和使用指定服务器指定规则在自己的域名运营商处添加域名解析 第二部分shell编码实现frp自由切换服务器并更新dns解析获取切换服务器参数脚本实现切换脚本更新DNS解析打开openwrt计…

MySQL — InnoDB事务

文章目录 事务定义事务特性事务隔离级别READ UNCOMMITTEDREPEATABLE READREAD COMMITTEDSERIALIZABLE 事务存在的问题脏读&#xff08;Dirty Read&#xff09;不可重复读&#xff08;Non-repeatable Read&#xff09;幻读&#xff08;Phantom Read&#xff09; 事务定义 数据库…

(十三)大数据实战——hadoop集群之YARN高可用实现自动故障转移

前言 本节内容是关于hadoop集群下yarn服务的高可用搭建&#xff0c;以及其发生故障转移的处理&#xff0c;同样需要依赖zookeeper集群的实现&#xff0c;实现该集群搭建时&#xff0c;我们要预先保证zookeeper集群是启动状态。yarn的高可用同样依赖zookeeper的临时节点及监控&…

构建器/建造者/构建者模式(C++)

定义 将一个复杂对象的构建与其表示相分离,使得同样的构建过程(稳定)可以创建不同的表示(变化)。 应用场景 在软件系统中&#xff0c;有时候面临着“一个复杂对象”的创建工作&#xff0c;其通常由各个部分的子对象用一定的算法构成;由于需求的变化&#xff0c;这个复杂对象…

Android应用开发(6)TextView进阶用法

Android应用开发学习笔记——目录索引 上一章Android应用开发&#xff08;5&#xff09;文本视图&#xff08;TextView&#xff09;介绍了文本视图&#xff08;TextView&#xff09;设置文本内容、设置文本大小、设置文本显示颜色。 TextView是最基础的文本显示控件&#xff…

PHP正则绕过解析

正则绕过 正则表达式PHP正则回溯PHP中的NULL和false回溯案例案例1案例2 正则表达式 在正则中有许多特殊的字符&#xff0c;不能直接使用&#xff0c;需要使用转义符\。如&#xff1a;$,(,),*,,.,?,[,,^,{。 这里大家会有疑问&#xff1a;为啥小括号(),这个就需要两个来转义&a…

Linux 下设置开机自启动的方法

文章目录 事先准备对于普通的 Linux对于 RedHat Enterprise Linux 9 笔者的运行环境&#xff1a; 设置成功过的 Linux&#xff1a; RedHat Enterprise Linux 9 x86_64 CentOS 8 x86_64 事先准备 进行这个教程之前&#xff0c;必须要先安装好一个 Linux 操作系统。这个 Linux…

JavaWeb 手写Tomcat底层机制

目录 一、Tomcat底层整体架构 1.简介 : 2.分析图 : 3.基于Socket开发服务端的流程 : 4.打通服务器端和客户端的数据通道 : 二、多线程模型的实现 1.思路分析 : 2.处理HTTP请求 : 3.自定义Tomcat : 三、自定义Servlet规范 1. HTTP请求和响应 : 1 CyanServletRequest …

《面试1v1》ElasticSearch基础

&#x1f345; 作者简介&#xff1a;王哥&#xff0c;CSDN2022博客总榜Top100&#x1f3c6;、博客专家&#x1f4aa; &#x1f345; 技术交流&#xff1a;定期更新Java硬核干货&#xff0c;不定期送书活动 &#x1f345; 王哥多年工作总结&#xff1a;Java学习路线总结&#xf…

Kafka的配置和使用

目录 1.服务器用docker安装kafka 2.springboot集成kafka实现生产者和消费者 1.服务器用docker安装kafka ①、安装docker&#xff08;docker类似于linux的软件商店&#xff0c;下载所有应用都能从docker去下载&#xff09; a、自动安装 curl -fsSL https://get.docker.com | b…

Visual Studio配置PCL库

Visual Studio配置PCL库 Debug和Release配置新建项目配置属性表测试参考 Debug和Release Debug和Release的配置过程一模一样&#xff0c;唯一区别就在于最后一步插入的附加依赖项不同&#xff0c;因此下面以debug为例。 配置新建项目 1、新建一个C空项目&#xff0c;模式设置…

Linux文本三剑客之awk

目录 前言 awk 1.认识awk 2.使用awk 2.1语法 2.2常用命令选项 2.3awk变量 2.3.1内置变量 2.3.2自定义变量 2.4printf命令 awk例题 前言 awk、grep、sed是linux操作文本的三大利器&#xff0c;合称文本三剑客&#xff0c;也是必须掌握的linux命令之一。三者的功能都是…

FreeRTOS源码分析-9 互斥信号量

目录 1 优先级翻转问题 2 互斥信号量概念及其应用 2.2FreeRTOS互斥信号量介绍 2.3FreeRTOS互斥信号量工作原理 3 互斥信号量函数应用 3.1功能分析 3.2API详解 3.3功能实现 4 递归互斥信号量函数应用 4.1死锁现象 ​编辑 4.2API详解 4.3解决死锁 5 互斥信号量实现原…

C++数据结构之平衡二叉搜索树(一)——AVL的实现(zig与zag/左右双旋/3+4重构)

本文目录 00.BBST——平衡二叉搜索树01.AVL树02.AVL的插入2.1单旋——zig 与 zag2.2插入节点后的单旋实例2.3手玩小样例2.4双旋实例2.5小结 03.AVL的删除3.1单旋删除3.2双旋删除3.3小结 04.34重构05.综合评价AVL5.1优点5.2缺点 06.代码注意插入算法删除算法完整代码&#xff1a…

关于接口测试用例设计的一些思考

接口测试发现的典型问题 传入参数处理不当&#xff0c;引起程序错误类型溢出&#xff0c;导致数据读取和写入不一致对象权限校验出错&#xff0c;可获取其他角色信息状态出错&#xff0c;导致逻辑处理出现问题逻辑校验不完善定时任务执行出错 接口测试用例设计 接口测试用例…

redis入门3-在java中操作redis

Redis的java客户端 Jedis、Lettuce、Redisson、以及spring提供的spring data redis Jedis操作redis //添加依赖 <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.8.0</version> </dep…

JJWT快速入门

本篇介绍使用 JJWT&#xff08;Java JWT&#xff09;库来生成 JWT Token&#xff0c;步骤如下&#xff1a; 添加依赖&#xff1a; 在项目中添加 JJWT 依赖项。对于 Maven 项目&#xff0c;可以在 pom.xml 文件中添加以下依赖项&#xff1a; <dependency><groupId>…

python解析帆软cpt及frm文件(xml)获取源数据表及下游依赖表

#!/user/bin/evn python import os,re,openpyxl 输入&#xff1a;帆软脚本文件路径输出&#xff1a;帆软文件检查结果Excel#获取来源表 def table_scan(sql_str):# remove the /* */ commentsq re.sub(r"/\*[^*]*\*(?:[^*/][^*]*\*)*/", "", sql_str)# r…

c++学习(特殊类设计)[30]

只能在堆上创建对象的类 如果你想要确保对象只能在堆上创建&#xff0c;可以通过将析构函数声明为私有&#xff0c;并提供一个静态成员函数来创建对象。这样&#xff0c;类的实例化只能通过调用静态成员函数来完成&#xff0c;而无法直接在栈上创建对象。 以下是一个示例&…