Appearance
备忘录模式(Memento Pattern)笔记
定义
备忘录模式是一种行为设计模式,它允许在不破坏封装性的前提下,捕获并外部化一个对象的内部状态,以便稍后可以将该对象恢复到原先保存的状态。
英文原文定义:
The Memento Pattern captures and externalizes an object's internal state without violating encapsulation so that the object can be restored to this state later.
核心概念
备忘录模式通过引入"备忘录"对象来保存和恢复原始对象的状态,同时保持封装边界不被破坏。该模式涉及三个关键角色协同工作。
主要角色
- Originator(原发器):需要保存状态的对象,创建备忘录并可从备忘录恢复
- Memento(备忘录):存储原发器内部状态的对象
- Caretaker(管理者):负责保存备忘录,但不对备忘录内容进行操作
优缺点
优点
- 状态封装:不破坏封装性的情况下保存对象状态
- 简化原发器:状态保存和恢复逻辑从原发器中分离
- 时间快照:支持任意时间点的状态恢复
- 历史管理:可以维护多个历史状态
缺点
- 资源消耗:频繁保存状态可能消耗大量内存
- 大对象开销:大型对象的备忘录可能占用显著内存
- 性能影响:状态序列化/反序列化可能影响性能
- 暴露细节风险:不当实现可能间接暴露私有状态
解决的问题
备忘录模式主要解决以下经典问题:
- 撤销操作:需要实现撤销功能时
- 状态回滚:需要回滚到先前状态时
- 事务回退:事务处理需要回退到特定检查点时
- 快照功能:需要生成对象状态快照时
- 游戏存档:游戏进度保存与加载
实现注意事项
状态范围:
- 完整状态:保存对象全部状态
- 增量状态:仅保存变化的部分状态
存储方式:
- 内存存储:适合短期、频繁的撤销操作
- 持久化存储:适合长期存档需求
版本控制:
- 简单栈式:仅支持顺序撤销
- 分支管理:支持多版本跳转
性能优化:
- 懒加载:仅在需要时加载状态
- 压缩存储:对状态数据进行压缩
线程安全:
- 多线程环境下状态保存需要同步
- 考虑深拷贝避免引用问题
实现变体
严格封装:
- 仅原发器可访问备忘录内容
- 管理者只能存储不能查看
接口暴露:
- 定义窄接口供管理者使用
- 宽接口仅原发器可见
持久化备忘录:
- 备忘录可序列化到磁盘
- 支持跨会话状态恢复
增量备忘录:
- 仅存储状态变化部分
- 减少内存使用
与其他模式的关系
相似模式区分
命令模式:
- 备忘录:用于保存和恢复状态
- 命令:用于封装操作请求
原型模式:
- 备忘录:保存特定时刻状态
- 原型:复制当前对象状态
状态模式:
- 备忘录:保存对象状态
- 状态:封装与状态相关的行为
常见搭配组合
- 备忘录 + 命令:实现完善的撤销机制
- 备忘录 + 迭代器:遍历状态历史
- 备忘录 + 原型:高效创建状态副本
- 备忘录 + 观察者:状态变化时自动保存
典型应用场景
- 文本编辑器的撤销功能
- 游戏进度存档/读档
- 数据库事务回滚
- 配置管理工具
- 绘图软件的历史记录
- 虚拟机快照功能
代码示例(文本编辑器实现)
java
// 备忘录接口(窄接口)
interface TextMemento {
// 空接口,仅作为标记
}
// 原发器:文本编辑器
class TextEditor {
private StringBuilder text = new StringBuilder();
// 创建备忘录
public TextMemento save() {
return new TextMementoImpl(text.toString());
}
// 从备忘录恢复
public void restore(TextMemento memento) {
if (memento instanceof TextMementoImpl) {
this.text = new StringBuilder(((TextMementoImpl)memento).getState());
}
}
public void write(String text) {
this.text.append(text);
}
public void print() {
System.out.println("Current text: " + text);
}
// 具体备忘录实现(私有内部类)
private static class TextMementoImpl implements TextMemento {
private final String state;
public TextMementoImpl(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
}
// 管理者:历史记录
class History {
private Stack<TextMemento> states = new Stack<>();
public void push(TextMemento memento) {
states.push(memento);
}
public TextMemento pop() {
return states.pop();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
TextEditor editor = new TextEditor();
History history = new History();
editor.write("Hello ");
history.push(editor.save());
editor.print(); // 输出: Current text: Hello
editor.write("World!");
history.push(editor.save());
editor.print(); // 输出: Current text: Hello World!
// 撤销
editor.restore(history.pop());
editor.print(); // 输出: Current text: Hello
// 再次撤销
editor.restore(history.pop());
editor.print(); // 输出: Current text:
}
}