首页/开发/代码质量与评审速查/备忘录模式 Memento(行为型)
设计原则与模式62 条

备忘录模式 Memento(行为型)

在不破坏封装的前提下捕获并恢复对象内部状态:撤销、快照、回滚场景;快照应不可变且控制体积,频繁快照要评估内存与序列化成本。

备忘录模式解决什么

备忘录模式(Memento)在不破坏封装的前提下捕获对象内部状态,并在需要时把对象恢复到该状态。它让撤销、快照与回滚成为可能,代价是状态副本占用资源。使用时有两个约束:快照本身应当不可变,避免后续操作把快照内容改掉;快照数量要有上限与淘汰策略,否则内存会随操作次数持续增长。

三个角色

原发器持有可变化的内部状态,负责产生与恢复快照
快照状态副本,应当不可变,不对外暴露内部引用
保管者负责保存快照序列,不解读快照内容
常见变体增量快照、命令与快照组合实现撤销重做

典型使用场景

  • 文本编辑器撤销:每次编辑前保存光标与文本状态,撤销时回到上一快照
  • 配置变更回滚:改动前保存基准配置,验证失败后一键恢复
  • 批量导入预备份:导入前记录关键记录状态,异常时回滚到导入前
  • 工作流引擎:环节推进时保存上下文,失败重试从最近检查点继续
  • 交互式工具:支持撤销重做的图形或数据编辑场景

基本形态

基本形态
class Editor {
    private String text;
    private int cursor;

    Memento save() {                  // 产生不可变快照
        return new Memento(text, cursor);
    }

    void restore(Memento m) {         // 从快照恢复
        this.text = m.text();
        this.cursor = m.cursor();
    }
}

实现要点对照

要点正确做法常见错误
快照内容保存值或不可变副本直接持有原对象引用,后续修改污染快照
快照数量设定上限并按策略淘汰无限追加,长时运行内存持续增长
暴露程度只允许原发器解读内容把内部列表直接暴露,调用方可随意篡改
序列化按需选择内存快照或序列化存储一律序列化,小改动也付出高成本
并发修改快照期间锁定或复制恢复时发现状态已被其他线程改变
快照粒度决定可用性与成本:全量快照实现简单但占用大,增量快照省内存但恢复逻辑复杂且容易出错。选择时看状态规模与撤销频率,不要先上最复杂的方案。
撤销栈与快照列表通常需要上限:常见做法是限定步数(例如保留最近五十步)或按内存占用淘汰。没有上限的实现往往在长时间使用后出现内存问题,而这类问题不易在测试中暴露。

落地检查清单

  • 快照不可变,恢复过程不会反过来修改快照
  • 快照保存的是值或副本,而不是可变对象的引用
  • 快照数量有上限与淘汰策略
  • 恢复后对象内部一致性得到保证,关联结构同步还原

易错点

  • 快照持有对象引用而非值拷贝:恢复后发现状态仍被后续修改污染,撤销行为不可预期
  • 无限快照历史:内存随操作次数增长,长时间运行后出现内存压力甚至进程被杀
  • 恢复只还原部分状态:相关联的索引或缓存未同步回滚,对象进入不一致状态
  • 把快照内容直接暴露给调用方:外部可修改快照,撤销结果取决于调用方行为

数据来源与参考

  • 《设计模式》(GoF)· Memento
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌