19.设计模式-MEMENTO(备忘录)

一、模式定义与核心思想

备忘录模式是一种行为型设计模式,其核心目标是通过在不破坏对象封装性的前提下,捕获并保存其内部状态,并在需要时恢复至历史状态。该模式通过将状态管理与业务逻辑解耦,赋予系统以下核心能力:

  1. 状态回溯:支持撤销(Undo)、重做(Redo)操作(如文本编辑器的撤销功能)。
  2. 历史快照:保存对象的关键状态快照(如游戏存档、数据库事务回滚)。
  3. 封装保护:避免外部直接访问对象内部状态,确保数据安全性。

核心设计哲学

  • 状态隔离:通过独立备忘录对象存储状态,避免业务对象与状态管理逻辑耦合。
  • 职责分离:发起者(Originator)专注于业务逻辑,管理者(Caretaker)专注状态生命周期管理。

典型应用场景

  • 文本编辑器的多级撤销/重做
  • 游戏存档与读档系统
  • 数据库事务的原子性保障
  • 浏览器历史记录导航

二、模式组成与UML类图

核心角色
  1. Originator(发起者)
    • 负责创建备忘录对象(createMemento()),保存当前状态。
    • 提供状态恢复接口(restore(memento)),从备忘录加载历史状态。
  1. Memento(备忘录)
    • 封装Originator的内部状态(如字段值、配置参数)。
    • 对外仅暴露窄接口(如只读属性),保护数据完整性。
  1. Caretaker(管理者)
    • 维护备忘录集合(如栈、列表),支持多版本状态管理。
    • 不直接操作备忘录内容,仅负责存储与传递。
UML类图
classDiagram
    class Originator {
        -state: String
        +createMemento(): Memento
        +restore(memento: Memento): void
    }

    class Memento {
        -state: String
        +getState(): String
    }

    class Caretaker {
        -mementos: List<Memento>
        +save(memento: Memento): void
        +retrieve(index: int): Memento
    }

    Originator --> Memento : 创建/恢复
    Caretaker --> Memento : 存储/检索


三、代码实现示例

场景:实现文本编辑器的多级撤销功能

1. 备忘录与发起者类
// 备忘录类(仅暴露只读接口)
public class TextMemento {
    private final String content;
    public TextMemento(String content) { this.content = content; }
    public String getContent() { return content; }
}

// 发起者类(文本编辑器)
public class TextEditor {
    private String content;

    public void write(String text) { content += text; }

    public TextMemento save() {
        return new TextMemento(content);
    }

    public void restore(TextMemento memento) {
        this.content = memento.getContent();
    }

    public void print() {
        System.out.println("当前内容: " + content);
    }
}
2. 管理者类与客户端调用
// 管理者类(历史记录栈)
public class History {
    private Stack<TextMemento> stack = new Stack<>();

    public void push(TextMemento memento) {
        stack.push(memento);
    }

    public TextMemento pop() {
        return stack.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()); // 保存状态1
        editor.write(" World!");     
        history.push(editor.save()); // 保存状态2

        editor.print(); // 输出: 当前内容: Hello World!

        // 撤销到上一个状态
        editor.restore(history.pop());
        editor.print(); // 输出: 当前内容: Hello
    }
}

四、工业级源码应用

  1. Java AWT/Swing撤销框架
    • UndoManager类通过备忘录模式管理GUI操作历史,支持undo()redo()
  1. Spring框架事务管理
    • @Transactional注解通过备忘录保存数据库连接状态,异常时回滚至事务开始点。
  1. 游戏引擎存档系统
    • Unity的PlayerPrefs和Unreal Engine的SaveGame系统利用备忘录模式序列化游戏对象状态。
  1. 浏览器历史记录
    • Chrome的History API通过栈结构存储页面状态,支持前进/后退导航。
  1. 数据库事务日志
    • MySQL的InnoDB引擎使用REDO/UNDO日志实现ACID特性,本质是备忘录模式的扩展。

五、模式优劣与最佳实践

优势

  • 状态安全:通过私有构造函数和接口限制,防止外部篡改历史数据。
  • 扩展灵活:支持多级撤销、分支存档等复杂场景(如Git版本控制)。

局限性

  • 内存消耗:频繁保存大对象可能导致内存溢出(需结合序列化存储优化)。
  • 性能损耗:高频率状态保存场景需权衡性能(如实时游戏引擎)。

最佳实践

  1. 增量快照:仅保存变化的部分(如Vue.js的虚拟DOM Diff算法)。
  2. 懒加载策略:延迟加载非活跃状态的备忘录(如浏览器标签页冻结)。
  3. 结合原型模式:通过深拷贝减少状态复制的开销。

总结

备忘录模式如同软件系统的“时间机器”,通过状态快照化历史可追溯的设计理念,在编辑器、游戏、数据库等领域展现了强大的生命力。其核心价值在于将状态管理业务逻辑解耦,开发者需重点把控备忘录的存储粒度与性能平衡,结合具体场景选择内存快照或持久化存储策略,从而构建出高可靠、易维护的系统架构。

posted @ 2025-04-12 10:56  雾里看花的少年  阅读(45)  评论(0)    收藏  举报