Java 设计模式之备忘录模式:优雅保存与恢复对象状态
·
公众号:【CTO成长日记】
Java 设计模式之备忘录模式:优雅保存与恢复对象状态
在软件开发中,我们经常需要保存对象的中间状态,以便在需要时能够恢复到该状态。例如文本编辑器的撤销功能、游戏中的存档机制、事务的回滚操作等。备忘录模式(Memento Pattern)正是为这类场景设计的,它提供了一种在不破坏封装性的前提下,捕获和恢复对象状态的机制。
备忘录模式的核心思想
备忘录模式属于行为型设计模式,其核心思想是在不暴露对象内部状态的情况下,捕获并保存对象的内部状态,以便后续需要时能将对象恢复到之前的状态。
这种设计的好处是:
- 保持对象状态的封装性,不暴露内部实现细节
- 提供了状态恢复机制,支持撤销操作
- 简化了原发器对象,将状态保存和恢复的逻辑分离出去
备忘录模式的角色组成
备忘录模式主要包含三个核心角色:
- 原发器(Originator):需要保存状态的对象,能创建备忘录并从备忘录恢复状态
- 备忘录(Memento):存储原发器的内部状态,对其他对象隐藏具体实现
- 负责人(Caretaker):负责管理备忘录,提供保存和获取备忘录的接口,但不操作备忘录的内容
备忘录模式的代码实现
下面我们通过一个文本编辑器的例子来实现备忘录模式,该编辑器需要支持文本内容的保存和撤销功能。
1. 定义备忘录类
备忘录类用于存储原发器的状态,为了保证封装性,它通常只允许原发器访问其内部状态,对其他对象隐藏细节。
// 备忘录类:存储文本编辑器的状态
public class EditorMemento {
private final String content; // 保存的文本内容
private final int cursorPosition; // 保存的光标位置
// 构造方法私有,只能由原发器创建
public EditorMemento(String content, int cursorPosition) {
this.content = content;
this.cursorPosition = cursorPosition;
}
// 只提供给原发器访问的 getter 方法
String getContent() {
return content;
}
int getCursorPosition() {
return cursorPosition;
}
}
2. 定义原发器类
原发器是需要保存状态的对象,它负责创建备忘录(保存当前状态)和从备忘录恢复状态。
// 原发器:文本编辑器
public class TextEditor {
private String content; // 当前文本内容
private int cursorPosition; // 当前光标位置
public TextEditor() {
this.content = "";
this.cursorPosition = 0;
}
// 输入文本
public void type(String text) {
content = content.substring(0, cursorPosition) + text + content.substring(cursorPosition);
cursorPosition += text.length();
}
// 移动光标
public void moveCursor(int position) {
if (position >= 0 && position <= content.length()) {
this.cursorPosition = position;
}
}
// 创建备忘录,保存当前状态
public EditorMemento save() {
return new EditorMemento(content, cursorPosition);
}
// 从备忘录恢复状态
public void restore(EditorMemento memento) {
this.content = memento.getContent();
this.cursorPosition = memento.getCursorPosition();
}
// 辅助方法:显示当前状态
public void showState() {
System.out.println("文本内容:" + content);
System.out.println("光标位置:" + cursorPosition);
System.out.println("---------------------");
}
// getter 方法
public String getContent() {
return content;
}
public int getCursorPosition() {
return cursorPosition;
}
}
3. 定义负责人类
负责人负责管理备忘录,它可以保存多个备忘录,提供保存和获取备忘录的接口,但不能查看或修改备忘录的内容。
// 负责人:管理备忘录
import java.util.ArrayList;
import java.util.List;
public class EditorHistory {
private List<EditorMemento> mementos = new ArrayList<>();
// 保存备忘录
public void saveMemento(EditorMemento memento) {
mementos.add(memento);
}
// 获取上一个备忘录(撤销操作)
public EditorMemento getPreviousMemento() {
if (mementos.size() == 0) {
return null;
}
// 移除并返回最后一个备忘录
int lastIndex = mementos.size() - 1;
EditorMemento lastMemento = mementos.get(lastIndex);
mementos.remove(lastIndex);
return lastMemento;
}
}
4. 客户端使用示例
public class Client {
public static void main(String[] args) {
// 创建文本编辑器和历史记录管理器
TextEditor editor = new TextEditor();
EditorHistory history = new EditorHistory();
// 第一次操作
editor.type("Hello, ");
history.saveMemento(editor.save()); // 保存状态
System.out.println("第一次操作后:");
editor.showState();
// 第二次操作
editor.type("World!");
history.saveMemento(editor.save()); // 保存状态
System.out.println("第二次操作后:");
editor.showState();
// 第三次操作
editor.moveCursor(7);
editor.type("Java ");
history.saveMemento(editor.save()); // 保存状态
System.out.println("第三次操作后:");
editor.showState();
// 第一次撤销
editor.restore(history.getPreviousMemento());
System.out.println("第一次撤销后:");
editor.showState();
// 第二次撤销
editor.restore(history.getPreviousMemento());
System.out.println("第二次撤销后:");
editor.showState();
}
}
运行结果:
第一次操作后:
文本内容:Hello,
光标位置:7
---------------------
第二次操作后:
文本内容:Hello, World!
光标位置:13
---------------------
第三次操作后:
文本内容:Hello, Java World!
光标位置:12
---------------------
第一次撤销后:
文本内容:Hello, World!
光标位置:13
---------------------
第二次撤销后:
文本内容:Hello,
光标位置:7
---------------------
Java 中的备忘录模式应用
在 Java 标准库和常见框架中,备忘录模式的思想也有广泛应用:
java.util.Date:Date类的clone()方法可以创建对象的副本,本质上是备忘录的一种简化实现java.io.Serializable:通过序列化可以保存对象的完整状态,需要时再反序列化恢复,这是一种广义的备忘录实现- Spring 的事务管理:Spring 的事务回滚机制本质上使用了备忘录模式的思想,保存事务开始前的状态,以便在需要时恢复
- 编辑器和 IDE 的撤销功能:几乎所有文本编辑器和 IDE 都实现了撤销 / 重做功能,底层通常使用备忘录模式
备忘录模式的变种
根据实际需求,备忘录模式有几种常见变种:
- 增量备忘录:只记录与上一个状态的差异,而不是完整状态,适用于状态较大的场景
- 带时间戳的备忘录:为每个备忘录添加时间戳,支持按时间点恢复
- 限制备忘录数量:当备忘录数量达到上限时,移除最早的备忘录,适用于内存有限的场景
- 可重做的备忘录:维护两个栈(撤销栈和重做栈),支持撤销和重做操作
备忘录模式的适用场景
- 当需要保存和恢复对象的中间状态(如撤销操作)时
- 当不希望暴露对象的内部状态,同时又需要获取其状态时
- 当对象的状态变化频繁,且需要记录状态变化历史时
- 当需要实现快照功能,以便后续可以回到指定时间点的状态时
备忘录模式的优缺点
优点:
- 保持了对象状态的封装性,避免了暴露内部实现细节
- 提供了状态恢复机制,支持灵活的撤销操作
- 简化了原发器的设计,将状态管理逻辑分离出去
- 便于维护状态历史,支持多步撤销
缺点:
- 如果对象状态较大,频繁创建备忘录会消耗较多内存
- 负责人需要管理备忘录的生命周期,增加了系统复杂度
- 如果对象状态包含不可序列化的资源,备忘录的实现会变得复杂
总结
备忘录模式通过分离对象状态的保存和恢复逻辑,在不破坏封装性的前提下,为对象提供了灵活的状态管理机制。它特别适合需要实现撤销功能、状态快照或事务回滚的场景。
在实际开发中,我们需要根据对象状态的大小和变化频率,权衡内存消耗和功能需求,选择合适的备忘录实现方式。理解备忘录模式的设计思想,有助于我们设计出更灵活、更健壮的状态管理机制。
原文:CTO成长日记
更多推荐



所有评论(0)