Java设计模式之备忘录模式详解
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()); // 保存状态1editor.type("备忘录");history.push(editor.save()); // 保存状态2editor.type("示例");System.out.println("\n当前状态:");editor.printStatus();// 撤销到上一步System.out.println("\n撤销操作:");editor.restore(history.pop()); // 恢复状态2editor.printStatus();// 再撤销一步System.out.println("\n再次撤销:");editor.restore(history.pop()); // 恢复状态1editor.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);}
}