Java设计模式之备忘录模式详解
一、备忘录模式核心思想
核心目标:捕获对象内部状态并在需要时恢复,同时不破坏对象的封装性。如同游戏存档系统,允许玩家保存当前进度并在需要时回退到之前的状态。
二、备忘录模式类图(Mermaid)
三、代码实现示例
1. 文本编辑器撤销功能
// 备忘录类(存储编辑器状态)
class TextMemento {
private final String text;
private final int cursorPosition;
public TextMemento(String text, int cursorPosition) {
this.text = text;
this.cursorPosition = cursorPosition;
}
public String getText() {
return text;
}
public int getCursorPosition() {
return cursorPosition;
}
}
// 原发器:文本编辑器
class TextEditor {
private StringBuilder text = new StringBuilder();
private int cursorPosition = 0;
public void type(String words) {
text.insert(cursorPosition, words);
cursorPosition += words.length();
System.out.println("当前文本: " + text);
}
public void moveCursor(int position) {
cursorPosition = Math.max(0, Math.min(position, text.length()));
System.out.println("光标移动到: " + cursorPosition);
}
public TextMemento save() {
return new TextMemento(text.toString(), cursorPosition);
}
public void restore(TextMemento memento) {
this.text = new StringBuilder(memento.getText());
this.cursorPosition = memento.getCursorPosition();
System.out.println("恢复文本: " + text);
}
public void printStatus() {
System.out.println("文本: " + text);
System.out.println("光标位置: " + cursorPosition);
}
}
// 管理者:历史记录
class History {
private List<TextMemento> states = new ArrayList<>();
public void push(TextMemento state) {
states.add(state);
}
public TextMemento pop() {
if (states.isEmpty()) return null;
return states.remove(states.size() - 1);
}
public TextMemento get(int index) {
return states.get(index);
}
}
// 客户端调用
public class Client {
public static void main(String[] args) {
TextEditor editor = new TextEditor();
History history = new History();
// 编辑文本
editor.type("设计模式");
history.push(editor.save()); // 保存状态1
editor.type("备忘录");
history.push(editor.save()); // 保存状态2
editor.type("示例");
System.out.println("\n当前状态:");
editor.printStatus();
// 撤销到上一步
System.out.println("\n撤销操作:");
editor.restore(history.pop()); // 恢复状态2
editor.printStatus();
// 再撤销一步
System.out.println("\n再次撤销:");
editor.restore(history.pop()); // 恢复状态1
editor.printStatus();
}
}
四、模式优缺点分析
✅ 优势
- 状态封装:不暴露对象内部实现细节
- 撤销/重做支持:轻松实现历史记录功能
- 状态快照:支持任意时刻状态保存
- 符合单一职责:状态管理职责分离
❌ 缺点
- 内存消耗:大量状态保存可能导致内存占用高
- 性能影响:大对象状态保存/恢复可能耗时
- 复杂状态处理:嵌套对象状态保存较复杂
五、典型应用场景
- 文本编辑器:撤销/重做功能实现
- 游戏开发:保存/加载游戏进度
- 事务回滚:数据库操作回退
- 软件配置:保存和恢复用户设置
- 绘图软件:操作历史记录
- 状态机:回退到之前状态
六、Mermaid序列图(状态保存与恢复)
七、备忘录模式 vs 其他模式
对比模式 | 核心区别 |
---|---|
命令模式 | 封装操作请求,可支持撤销 |
状态模式 | 对象行为随状态改变 |
原型模式 | 克隆对象而非保存状态 |
八、实际框架应用案例
1. Java Swing的UndoManager
2. Spring框架的事务管理
@Transactional
public void transferMoney(Account from, Account to, double amount) {
// 事务开始时创建备忘录(保存点)
savepoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
try {
from.withdraw(amount);
to.deposit(amount);
} catch (Exception e) {
// 回滚到保存点
TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savepoint);
}
}
九、高级应用技巧
1. 增量备忘录(节省内存)
class IncrementalMemento {
private final String diff; // 只存储变化部分
public IncrementalMemento(String diff) {
this.diff = diff;
}
public String apply(String base) {
// 应用差异到基础状态
return base + diff;
}
}
2. 多级撤销/重做栈
class HistoryManager {
private Stack<Memento> undoStack = new Stack<>();
private Stack<Memento> redoStack = new Stack<>();
public void save(Memento state) {
undoStack.push(state);
redoStack.clear();
}
public Memento undo() {
if (!undoStack.isEmpty()) {
Memento state = undoStack.pop();
redoStack.push(state);
return undoStack.isEmpty() ? null : undoStack.peek();
}
return null;
}
public Memento redo() {
if (!redoStack.isEmpty()) {
Memento state = redoStack.pop();
undoStack.push(state);
return state;
}
return null;
}
}
十、常见问题解答
Q1:如何保存复杂对象状态?
- 序列化:实现
Serializable
接口
class ComplexMemento implements Serializable {
private Object complexState;
}
Q2:如何处理外部资源引用?
使用深拷贝避免外部资源影响:
class ResourceMemento {
private Resource resourceCopy;
public ResourceMemento(Resource original) {
this.resourceCopy = original.deepCopy();
}
}
Q3:如何限制备忘录访问权限?
使用内部类实现封装:
class Originator {
private String state;
// 内部备忘录类
class Memento {
private String state;
private Memento(String state) {
this.state = state;
}
private String getState() {
return state;
}
}
public Memento save() {
return new Memento(state);
}
}