最近看到一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站
一、基础实现结构
- 角色定义与代码骨架
备忘录模式包含三个核心角色,其协作关系如下:
- Originator(发起人):需保存/恢复状态的对象,负责创建和使用备忘录。
- Memento(备忘录):存储发起人状态的载体,通常设计为不可变类。
- Caretaker(管理者):管理备忘录对象,不操作其内容。
代码示例(文本编辑器场景):
// 发起人:文本编辑器
public class Editor {private String content;// 创建备忘录public Memento save() {return new Memento(this.content);}// 从备忘录恢复状态public void restore(Memento memento) {this.content = memento.getState();}
}
// 备忘录:存储状态
public class Memento {private final String state; // 不可变状态public Memento(String state) { this.state = state; }public String getState() { return state; }
}
// 管理者:保存备忘录列表
public class History {private List mementos = new ArrayList<>();public void add(Memento memento) { mementos.add(memento); }public Memento get(int index) { return mementos.get(index); }
}
- 状态保存与恢复流程
// 客户端调用示例
Editor editor = new Editor();
History history = new History();
editor.setContent("Initial text");
history.add(editor.save()); // 保存状态1
editor.setContent("Modified text");
history.add(editor.save()); // 保存状态2
// 恢复到状态1
editor.restore(history.get(0));
System.out.println(editor.getContent()); // 输出:Initial text
二、高级实现技巧
- 封装性优化
- 黑箱实现:将
Memento
作为Originator
的内部类,限制外部访问。public class Editor {private String content;// 内部类备忘录public class EditorMemento {private final String state;private EditorMemento() { this.state = content; } // 仅内部可访问}public EditorMemento save() { return new EditorMemento(); } }
- 窄接口与宽接口:通过包级私有访问控制,仅允许
Originator
读取Memento
内部状态。
- 多状态管理
使用栈结构实现撤销/重做功能:
public class History {private Stack undoStack = new Stack<>();private Stack redoStack = new Stack<>();public void push(Memento memento) { undoStack.push(memento); }public Memento undo() { return undoStack.pop(); }public void redo(Memento memento) { redoStack.push(memento); }
}
- 资源优化
- 增量存储:仅保存状态差异而非全量数据。
- 序列化:通过
Serializable
接口持久化备忘录至文件或数据库。// 发起人支持序列化 public class SerializableOriginator extends Originator implements Serializable {private String state;// 实现序列化逻辑 }
三、实际应用示例
- 游戏存档系统
// 发起人:游戏角色
public class Gamer {private int level;private Inventory items;public GamerMemento save() {return new GamerMemento(level, items.clone());}public void restore(GamerMemento memento) {this.level = memento.getLevel();this.items = memento.getItems();}// 备忘录类(内部类)public static class GamerMemento {private final int level;private final Inventory items;// 构造函数与Getter}
}
- 数据库事务回滚
public class DatabaseTransaction {private TableData originalData;public TransactionMemento snapshot() {return new TransactionMemento(originalData.clone());}public void rollback(TransactionMemento memento) {this.originalData = memento.getData();}
}
四、测试与调试
- 单元测试要点
- 状态一致性验证:使用断言检查恢复后的状态是否与预期一致。
assertEquals("Initial text", editor.getContent());
- 异常场景测试:模拟空备忘录或非法恢复操作。
assertThrows(IllegalStateException.class, () -> editor.restore(null));
- 性能监控
- 内存占用分析:通过Profiler工具监控备忘录对象的数量与大小。
- 优化策略:限制备忘录历史数量(如仅保存最近10个状态)。
五、常见问题与解决方案
问题 | 解决方案 |
---|---|
内存溢出 | 使用LRU算法淘汰旧备忘录,或采用增量存储。 |
状态不一致 | 确保Memento 不可变,避免外部修改。 |
跨会话恢复失败 | 将备忘录序列化至持久化存储(如文件或数据库)。 |
六、总结
备忘录模式通过状态快照与封装隔离机制,为撤销、回滚等场景提供了灵活的解决方案。实现时需注重:
- 职责分离:明确
Originator
、Memento
、Caretaker
的边界。 - 资源管理:根据场景选择内存存储或持久化方案。
- 封装性保护:通过内部类或访问控制限制备忘录访问。
该模式广泛应用于编辑器、游戏、数据库等领域,是实现无副作用状态管理的核心工具。