【HeadFirst系列之HeadFirstJava】第17天之备忘录模式全解析:如何优雅地实现撤销、回滚与存档?(附 Java 实战)

devtools/2025/3/14 23:45:01/

Java 设计模式 | 备忘录模式(Memento Pattern)全解析 + 实战

在软件开发中,我们经常需要保存对象的历史状态,并在需要时恢复,例如:

  • 文本编辑器的撤销/重做功能
  • 游戏的存档/回档机制
  • 数据库事务的回滚

如果不加控制地使用对象副本来保存状态,不仅浪费内存,而且增加系统复杂度如何优雅地管理对象的历史状态?—— 备忘录模式(Memento Pattern)登场!

本篇文章基于《Head First 设计模式》,深入剖析 备忘录模式的核心概念、应用场景、优缺点、在 JDK 和 Spring 框架中的应用,并提供 Java 代码实战示例,帮助你掌握这一重要设计模式!🚀


📌 1. 为什么需要备忘录模式?(遇到的问题)

❌ 直接保存对象副本的问题

假设我们要在一个文本编辑器中实现“撤销”功能,最简单的方法是每次修改前保存一份对象副本

java">EditorState backup = editor.clone();

但这样的问题是:

  1. 增加内存开销:对象副本可能非常大,保存多个状态会占用大量内存。
  2. 破坏封装性:外部代码必须了解 EditorState 的内部细节,违反了 “信息隐藏” 原则。
  3. 难以维护:如果对象状态复杂,直接复制副本的方式会让代码变得臃肿。

👉 如何优雅地管理对象的历史状态,同时保持封装性?—— 备忘录模式登场!


📌 2. 备忘录模式是什么?(核心思想)

备忘录模式(Memento Pattern)用于 保存对象的某个历史状态,并在需要时恢复,而不暴露对象的实现细节

🔹 模式结构

备忘录模式三个核心角色 组成:

  1. 发起人(Originator): 需要保存状态的对象,提供创建和恢复备忘录的方法。
  2. 备忘录(Memento): 负责存储发起人的内部状态,保证状态的封装性。
  3. 管理者(Caretaker): 负责存储多个备忘录,并在需要时提供恢复功能。

📌 3. 备忘录模式的 Java 实现(代码实战)

🎯 需求:

我们实现一个文本编辑器,可以保存多个状态,并支持**撤销(Undo)**功能。

🚀 Step 1:定义 Memento(备忘录类)

java">// 备忘录类:存储 Editor 的状态
class Memento {private final String text;public Memento(String text) {this.text = text;}public String getText() {return text;}
}

🚀 Step 2:定义 Editor(发起人)

java">// 发起人:文本编辑器,创建和恢复备忘录
class Editor {private String text = "";public void type(String words) {text += words;}public String getText() {return text;}// 创建备忘录public Memento save() {return new Memento(text);}// 恢复备忘录public void restore(Memento memento) {this.text = memento.getText();}
}

🚀 Step 3:定义 Caretaker(管理者)

java">import java.util.Stack;// 管理者:负责存储和恢复备忘录
class Caretaker {private Stack<Memento> history = new Stack<>();public void saveState(Editor editor) {history.push(editor.save());}public void undo(Editor editor) {if (!history.isEmpty()) {editor.restore(history.pop());}}
}

🚀 Step 4:测试备忘录模式

java">public class MementoPatternDemo {public static void main(String[] args) {Editor editor = new Editor();Caretaker caretaker = new Caretaker();// 输入文字editor.type("Hello, ");caretaker.saveState(editor);editor.type("World!");caretaker.saveState(editor);System.out.println("当前内容:" + editor.getText()); // Hello, World!// 撤销一次caretaker.undo(editor);System.out.println("撤销一次:" + editor.getText()); // Hello, // 再次撤销caretaker.undo(editor);System.out.println("撤销两次:" + editor.getText()); // (空)}
}

📌 运行结果:

当前内容:Hello, World!
撤销一次:Hello, 
撤销两次:

📌 4. 备忘录模式的应用场景

适用于:

  • 文本编辑器的撤销/重做(如 Word、IDE 代码编辑器)
  • 游戏存档/回档(如 RPG 游戏)
  • 数据库事务回滚(如 Hibernate 的 savepoint 机制)
  • Web 浏览器的历史记录

不适用于:

  • 对象状态过于庞大(可能消耗大量内存)
  • 状态变化过于频繁(存储和恢复的成本较高)

📌 5. 备忘录模式在 JDK & Spring 中的应用

🔹 JDK 中的应用

  • Java 的 Serializable 接口 允许对象序列化成字节流,从而保存和恢复状态。
  • java.util.Stack 作为 Caretaker,可以存储多个状态,实现撤销功能。

🔹 Spring 框架中的应用

  • Spring 事务管理(Transaction Management)
    • Spring 通过 TransactionManager 维护数据库的回滚点,类似于备忘录模式中的 Caretaker
    • 事务的 savepoint 机制允许在事务中存储多个回滚点,并在异常发生时恢复

📌 6. 备忘录模式的优缺点

✅ 优点

封装性好:状态存储在 Memento 中,不影响 Originator 的实现细节。
支持撤销/回滚:可以轻松地恢复历史状态。
解耦管理者与发起人Caretaker 仅管理 Memento,不需要了解 Originator 细节。

❌ 缺点

可能消耗大量内存:如果对象状态过大,存储多个 Memento 可能导致内存占用过高。
状态变化过快时效率低:如果状态变化频繁,存储和恢复 Memento 的操作可能会影响性能。


📌 7. 总结

  • 备忘录模式适用于需要保存和恢复历史状态的场景,如撤销、游戏存档、事务回滚等。
  • 它封装了对象状态,保持封装性,避免直接暴露内部数据。
  • 在 JDK 和 Spring 事务管理中都有类似应用,值得深入理解和运用!

💡 你是否在项目中用过类似的设计?欢迎留言交流!🚀


http://www.ppmy.cn/devtools/167144.html

相关文章

远程监控项目描述以及总体框架

远程监控项目基于之前的本地渲染项目做了一个扩展。本地渲染项目没有涉及到解码部分&#xff0c;是直接从rv126拿到摄像头的vi数据&#xff0c;做转换就刷新到了上面去。 uvc摄像头用ffmpeg做推流&#xff0c;所以这个远程是先拿到我们这个uvc摄像头的数据进行解码才能刷新到网…

python之replace,strip,split命令

1. replace() 方法 功能&#xff1a;替换字符串中的指定子串 语法&#xff1a;str.replace(old, new[, count]) 特点&#xff1a; 全部替换&#xff08;默认&#xff09;或指定替换次数区分大小写返回新字符串&#xff0c;原字符串不变 示例&#xff1a; text "Hello…

Java 替换图片背景图为透明

通过java代码将一个表情包的背景替换为空白或者透明 以下代码都是通过 ai生成的&#xff0c;已测试好用。 import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; /**/*** Author xiaoli* Date 2025/2/24 1…

算法精讲 | 树(二):BFS层序遍历の魔法——像水波纹一样扫描整棵树

&#x1f3af; 算法精讲 | 树&#xff08;二&#xff09;&#xff1a;BFS层序遍历の魔法——像水波纹一样扫描整棵树 &#x1f4c5; 2025/03/11 || 推荐阅读时间 12分钟 &#x1f31f; 开篇故事 小明用DFS解二叉树的右视图总超时&#xff0c;直到他发现BFS层序遍历就像超市结账…

mysql的MGR

3.MGR(MySQL Group Replication) MySQL组复制是Mysql5.7推出的高可用方案&#xff0c;具备以下特性&#xff1a; 一致性高&#xff1a;数据复制基于paxos分布式公式算法&#xff0c;保证多个节点的一致性 容错性高&#xff1a;只要不是超过一半的节点宕机&#xff0c;就可以继续…

机器学习之正则化

在机器学习领域&#xff0c;模型的性能至关重要&#xff0c;而过拟合问题常常阻碍模型在实际应用中的表现。正则化技术应运而生&#xff0c;成为解决这一难题的有力武器。它主要分为参数正则化和经验正则化两大类别&#xff0c;核心目的在于遵循奥卡姆剃刀定律&#xff0c;使模…

大一新生备战蓝桥杯c/c++B组——2024年省赛真题解题+心得分享

一&#xff0c;握手问题 这个题用点像小学奥数&#xff0c;直接手算就行 答案&#xff1a;1204 二&#xff0c;小球反弹 这个题思路简单&#xff0c;但是运行会显示超时。在思考思考&#xff0c;后续补代码。 三&#xff0c;好数 思路一&#xff1a; #include <iostream&…

ChromeOS 133 版本更新

ChromeOS 133 版本更新 1. 增强托管用户的 Office 文件处理功能 从 ChromeOS 133 开始&#xff0c;托管用户 现在可以 无缝打开和编辑 Microsoft Office 文件&#xff08;Word、PowerPoint、Excel&#xff09;&#xff0c;无论他们使用的是 Microsoft 365&#xff08;Office …