Skip to content

备忘录模式(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.

核心概念

备忘录模式通过引入"备忘录"对象来保存和恢复原始对象的状态,同时保持封装边界不被破坏。该模式涉及三个关键角色协同工作。

主要角色

  1. Originator(原发器):需要保存状态的对象,创建备忘录并可从备忘录恢复
  2. Memento(备忘录):存储原发器内部状态的对象
  3. Caretaker(管理者):负责保存备忘录,但不对备忘录内容进行操作

优缺点

优点

  • 状态封装:不破坏封装性的情况下保存对象状态
  • 简化原发器:状态保存和恢复逻辑从原发器中分离
  • 时间快照:支持任意时间点的状态恢复
  • 历史管理:可以维护多个历史状态

缺点

  • 资源消耗:频繁保存状态可能消耗大量内存
  • 大对象开销:大型对象的备忘录可能占用显著内存
  • 性能影响:状态序列化/反序列化可能影响性能
  • 暴露细节风险:不当实现可能间接暴露私有状态

解决的问题

备忘录模式主要解决以下经典问题:

  1. 撤销操作:需要实现撤销功能时
  2. 状态回滚:需要回滚到先前状态时
  3. 事务回退:事务处理需要回退到特定检查点时
  4. 快照功能:需要生成对象状态快照时
  5. 游戏存档:游戏进度保存与加载

实现注意事项

  1. 状态范围

    • 完整状态:保存对象全部状态
    • 增量状态:仅保存变化的部分状态
  2. 存储方式

    • 内存存储:适合短期、频繁的撤销操作
    • 持久化存储:适合长期存档需求
  3. 版本控制

    • 简单栈式:仅支持顺序撤销
    • 分支管理:支持多版本跳转
  4. 性能优化

    • 懒加载:仅在需要时加载状态
    • 压缩存储:对状态数据进行压缩
  5. 线程安全

    • 多线程环境下状态保存需要同步
    • 考虑深拷贝避免引用问题

实现变体

  1. 严格封装

    • 仅原发器可访问备忘录内容
    • 管理者只能存储不能查看
  2. 接口暴露

    • 定义窄接口供管理者使用
    • 宽接口仅原发器可见
  3. 持久化备忘录

    • 备忘录可序列化到磁盘
    • 支持跨会话状态恢复
  4. 增量备忘录

    • 仅存储状态变化部分
    • 减少内存使用

与其他模式的关系

相似模式区分

  1. 命令模式

    • 备忘录:用于保存和恢复状态
    • 命令:用于封装操作请求
  2. 原型模式

    • 备忘录:保存特定时刻状态
    • 原型:复制当前对象状态
  3. 状态模式

    • 备忘录:保存对象状态
    • 状态:封装与状态相关的行为

常见搭配组合

  1. 备忘录 + 命令:实现完善的撤销机制
  2. 备忘录 + 迭代器:遍历状态历史
  3. 备忘录 + 原型:高效创建状态副本
  4. 备忘录 + 观察者:状态变化时自动保存

典型应用场景

  • 文本编辑器的撤销功能
  • 游戏进度存档/读档
  • 数据库事务回滚
  • 配置管理工具
  • 绘图软件的历史记录
  • 虚拟机快照功能

代码示例(文本编辑器实现)

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: 
    }
}