一、模式定义
备忘录模式(Memento Pattern) 是一种行为设计模式,用于在不破坏对象封装性的前提下,捕获并保存对象的内部状态,以便后续恢复。核心思想是 状态快照管理,常用于实现撤销/重做、事务回滚等功能。
二、核心组件
组件 | 作用 |
---|---|
Originator | 原始对象,产生状态快照 |
Memento | 备忘录对象,存储Originator的状态 |
Caretaker | 状态管理器,保存和管理备忘录历史 |
三、模式优势
状态封装:不暴露对象内部实现细节
历史管理:支持多级撤销/重做操作
时间回溯:实现任意时间点的状态恢复
职责分离:状态存储与业务逻辑解耦
四、真实场景案例:文档版本控制系统
场景需求
开发IDE需要实现:
文档编辑时自动保存历史版本
支持最多50个历史版本的存储
允许用户自由切换文档版本
版本元数据记录(时间、作者)
五、Java实现代码
1. 备忘录接口(扩展元数据支持)
java
public interface DocumentMemento {
String getVersionId();
LocalDateTime getCreateTime();
String getAuthor();
}
2. 原发器(文档对象)
java
public class Document {
private StringBuilder content = new StringBuilder();
private String currentAuthor;
public Document(String author) {
this.currentAuthor = author;
}
// 编辑文档
public void append(String text) {
content.append(text);
}
// 创建快照
public DocumentSnapshot createSnapshot() {
return new DocumentSnapshot(
UUID.randomUUID().toString(),
LocalDateTime.now(),
currentAuthor,
content.toString()
);
}
// 恢复快照
public void restore(DocumentMemento memento) {
if (memento instanceof DocumentSnapshot) {
DocumentSnapshot snapshot = (DocumentSnapshot) memento;
this.content = new StringBuilder(snapshot.getContent());
this.currentAuthor = snapshot.getAuthor();
}
}
// 内部备忘录实现
private static class DocumentSnapshot implements DocumentMemento {
private final String versionId;
private final LocalDateTime createTime;
private final String author;
private final String content;
public DocumentSnapshot(String versionId, LocalDateTime createTime,
String author, String content) {
this.versionId = versionId;
this.createTime = createTime;
this.author = author;
this.content = content;
}
// Getters实现...
}
}
3. 负责人(版本控制器)
java
public class VersionController {
private final Deque<DocumentMemento> history = new ArrayDeque<>();
private final Deque<DocumentMemento> redoStack = new ArrayDeque<>();
private static final int MAX_HISTORY = 50;
public void saveVersion(DocumentMemento memento) {
if (history.size() >= MAX_HISTORY) {
history.removeFirst();
}
history.push(memento);
redoStack.clear(); // 新操作后清空重做栈
}
public DocumentMemento undo() {
if (!history.isEmpty()) {
DocumentMemento current = history.pop();
redoStack.push(current);
return history.peekFirst(); // 返回前一个版本
}
return null;
}
public DocumentMemento redo() {
if (!redoStack.isEmpty()) {
DocumentMemento redo = redoStack.pop();
history.push(redo);
return redo;
}
return null;
}
public List<DocumentMemento> getVersionHistory() {
return new ArrayList<>(history);
}
}
4. 客户端使用
java
public class IDEApplication {
public static void main(String[] args) {
// 初始化文档和版本控制器
Document doc = new Document("DeveloperA");
VersionController controller = new VersionController();
// 首次保存
doc.append("初始项目结构\n");
controller.saveVersion(doc.createSnapshot());
// 编辑并保存版本
doc.append("添加用户模块\n");
controller.saveVersion(doc.createSnapshot());
doc.append("实现登录功能\n");
controller.saveVersion(doc.createSnapshot());
// 显示历史版本
System.out.println("=== 版本历史 ===");
controller.getVersionHistory().forEach(m ->
System.out.printf("[%s] %s - %s%n",
m.getVersionId().substring(0,8),
m.getCreateTime().format(DateTimeFormatter.ISO_LOCAL_TIME),
m.getAuthor())
);
// 执行撤销
System.out.println("\n=== 执行撤销 ===");
DocumentMemento undoVersion = controller.undo();
doc.restore(undoVersion);
System.out.println("当前内容:\n" + doc.getContent());
// 执行重做
System.out.println("\n=== 执行重做 ===");
DocumentMemento redoVersion = controller.redo();
doc.restore(redoVersion);
System.out.println("当前内容:\n" + doc.getContent());
}
}
六、运行结果
text
=== 版本历史 ===
[3b7f8a9c] 14:25:36.123 - DeveloperA
[2a6e5d8f] 14:25:35.987 - DeveloperA
[1c4d3b2a] 14:25:35.845 - DeveloperA
=== 执行撤销 ===
当前内容:
初始项目结构
添加用户模块
=== 执行重做 ===
当前内容:
初始项目结构
添加用户模块
实现登录功能
七、模式变体与优化
1. 增量快照
java
// 差异存储优化
public class DeltaMemento implements DocumentMemento {
private final String diff; // 差异内容
private final PatchType type; // 变更类型
public enum PatchType { INSERT, DELETE, REPLACE }
}
2. 加密快照
java
// 安全增强
public class SecureMemento implements DocumentMemento {
private byte[] encryptedData;
public SecureMemento(String content) {
this.encryptedData = AESUtil.encrypt(content);
}
public String getDecryptedContent(String key) {
return AESUtil.decrypt(encryptedData, key);
}
}
八、行业应用场景
场景 | 具体应用 | 优势体现 |
---|---|---|
图形编辑器 | 绘图步骤撤销/重做 | 支持复杂操作回溯 |
游戏存档 | 玩家进度保存与加载 | 实现任意时刻存档恢复 |
数据库事务 | 事务回滚机制 | 保证数据一致性 |
配置管理系统 | 配置变更历史追踪 | 快速回滚错误配置 |
浏览器历史记录 | 页面导航前进/后退 | 优化用户体验 |
九、最佳实践建议
快照频率控制
java
// 自动保存策略
public enum SavePolicy {
ON_CHANGE, // 每次变更保存
TIMED, // 定时保存
MANUAL // 手动保存
}
大对象处理
java
// 使用序列化存储快照
public class SerializedMemento implements Serializable {
private byte[] serializedData;
public SerializedMemento(Originator originator) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(originator);
this.serializedData = bos.toByteArray();
}
}
内存优化
java
// 软引用缓存
public class MemoryOptimizedCaretaker {
private List<SoftReference<DocumentMemento>> history = new ArrayList<>();
}
一句话总结
备忘录模式主要解决的是对象状态的保存和恢复问题,通过创建备忘录对象来存储和恢复对象的内部状态。