【设计模式】【行为型模式】备忘录模式(Memento)

发布于:2025-02-15 ⋅ 阅读:(12) ⋅ 点赞:(0)

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
👍 欢迎点赞、收藏、关注,跟上我的更新节奏
🎵 当你的天空突然下了大雨,那是我在为你炸乌云

一、入门

什么是备忘录模式?

备忘录模式(Memento Pattern)是一种行为设计模式,用于在不破坏封装性的前提下,捕获并外部化一个对象的内部状态,以便在需要时恢复该状态。它通常用于实现撤销操作或保存对象的历史状态。

为什么需要备忘录模式?

假设我们正在开发一个简单的文本编辑器,支持以下功能:

  1. 用户可以输入文本。
  2. 用户可以保存当前文本状态。
  3. 用户可以撤销操作,恢复到之前保存的状态。

我们实现的代码如下

// 文本编辑器类
class Editor {
    private String content;

    public void setContent(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }

    // 保存状态(直接返回内部状态)
    public String save() {
        return content;
    }

    // 恢复状态(直接设置内部状态)
    public void restore(String savedContent) {
        this.content = savedContent;
    }
}

客户端测试

// 客户端代码
public class TextEditorWithoutMemento {
    public static void main(String[] args) {
        Editor editor = new Editor();

        // 用户输入文本
        editor.setContent("First draft");
        System.out.println("Current Content: " + editor.getContent());

        // 保存状态
        String savedState = editor.save();

        // 用户修改文本
        editor.setContent("Second draft");
        System.out.println("Updated Content: " + editor.getContent());

        // 撤销操作,恢复到之前的状态
        editor.restore(savedState);
        System.out.println("Restored Content: " + editor.getContent());
    }
}

存在的问题

  1. 破坏封装性Editorcontent状态通过save()restore()方法直接暴露给外部,外部代码可以随意修改状态。
  2. 耦合性高:撤销逻辑直接依赖Editor的内部状态,如果Editor的状态结构发生变化,撤销逻辑也需要修改。
  3. 难以扩展:如果需要支持多次撤销(保存多个历史状态),代码会变得复杂且难以维护。

如何实现备忘录模式?

备忘录模式的核心角色

  1. Originator(发起人)
    • 负责创建备忘录,记录当前时刻的内部状态。
    • 可以使用备忘录恢复内部状态。
  2. Memento(备忘录)
    • 存储发起人的内部状态。
    • 通常只允许发起人访问其内部状态,以防止其他对象修改。
  3. Caretaker(管理者)
    • 负责保存备忘录,但不能修改或检查其内容。
    • 提供保存和恢复备忘录的功能。

【案例】文本编辑 - 改
在这里插入图片描述
Memento(备忘录)EditorMemento类,保存Editor的状态

class EditorMemento {
    private final String content;

    public EditorMemento(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }
}

Originator(发起人)Editor

class Editor {
    private String content;

    public void setContent(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }

    // 创建备忘录
    public EditorMemento save() {
        return new EditorMemento(content);
    }

    // 从备忘录恢复状态
    public void restore(EditorMemento memento) {
        this.content = memento.getContent();
    }
}

Caretaker(管理者)History

class History {
    private final List<EditorMemento> mementos = new ArrayList<>();

    public void push(EditorMemento memento) {
        mementos.add(memento);
    }

    public EditorMemento pop() {
        if (mementos.isEmpty()) {
            return null;
        }
        return mementos.remove(mementos.size() - 1);
    }
}

客户端代码

public class TextEditorWithMemento {
    public static void main(String[] args) {
        Editor editor = new Editor();
        History history = new History();

        // 用户输入文本
        editor.setContent("First draft");
        history.push(editor.save()); // 保存状态
        System.out.println("Current Content: " + editor.getContent());

        // 用户修改文本
        editor.setContent("Second draft");
        history.push(editor.save()); // 保存状态
        System.out.println("Updated Content: " + editor.getContent());

        // 用户再次修改文本
        editor.setContent("Third draft");
        System.out.println("Updated Content: " + editor.getContent());

        // 撤销操作
        editor.restore(history.pop()); // 恢复到第二次保存的状态
        System.out.println("Restored Content: " + editor.getContent());

        // 再次撤销操作
        editor.restore(history.pop()); // 恢复到第一次保存的状态
        System.out.println("Restored Content: " + editor.getContent());
    }
}

二、备忘录模式在框架源码中的运用

Spring 框架中的事务管理

Spring 框架中的事务管理也使用了备忘录模式的思想。在事务回滚时,Spring 需要恢复对象的状态。

备忘录(Memento)TransactionStatusTransactionStatus 保存了事务的当前状态
以下是精简版代码的

public interface TransactionStatus extends SavepointManager, Flushable {
    boolean isNewTransaction(); // 是否是新事务
    boolean hasSavepoint();     // 是否有保存点
    void setRollbackOnly();     // 标记事务为回滚
    boolean isRollbackOnly();   // 是否需要回滚
    boolean isCompleted();      // 事务是否已完成
}

管理者(Caretaker)PlatformTransactionManager
PlatformTransactionManager 是 Spring 事务管理的核心接口,负责管理事务的生命周期。它通过 getTransaction() 方法获取事务状态(TransactionStatus),并在事务提交或回滚时恢复状态。

public interface PlatformTransactionManager {
    TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
    void commit(TransactionStatus status) throws TransactionException;
    void rollback(TransactionStatus status) throws TransactionException;
}

发起人(Originator)TransactionTemplateSpring提供的一个工具类,用于简化事务的编程式管理。它封装了事务的执行逻辑,并在事务执行过程中创建和恢复事务状态。

public class TransactionTemplate extends DefaultTransactionDefinition implements TransactionOperations {
    private PlatformTransactionManager transactionManager;

    @Override
    public <T> T execute(TransactionCallback<T> action) throws TransactionException {
        // 获取事务状态
        TransactionStatus status = this.transactionManager.getTransaction(this);

        T result;
        try {
            // 执行业务逻辑
            result = action.doInTransaction(status);
        } catch (RuntimeException | Error ex) {
            // 回滚事务
            rollbackOnException(status, ex);
            throw ex;
        }

        // 提交事务
        this.transactionManager.commit(status);
        return result;
    }

    private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException {
        if (status.isNewTransaction()) {
            this.transactionManager.rollback(status);
        }
    }
}

三、总结

备忘录模式的优点

  1. 封装性好
    • 备忘录模式将对象的状态保存在一个独立的Memento对象中,避免了直接暴露对象的内部状态,保持了对象的封装性。
  2. 简化状态管理
    • 通过将状态管理的逻辑集中到Caretaker中,避免了状态管理代码分散在业务逻辑中,使代码更清晰、更易维护。
  3. 支持撤销/重做功能
    • 备忘录模式天然适合实现撤销和重做功能,通过保存多个 Memento 对象,可以轻松恢复到之前的状态。
  4. 扩展性强
    • 如果需要支持多次撤销或保存多个历史状态,只需在Caretaker中维护多个Memento对象即可。
  5. 解耦
    • 备忘录模式将状态保存和恢复的逻辑与业务逻辑解耦,使得代码更易于复用和扩展。

备忘录模式的缺点

  1. 内存消耗
    • 如果需要保存大量的状态或状态数据较大,备忘录模式可能会占用较多的内存。
  2. 性能开销
    • 频繁地创建和恢复Memento对象可能会带来一定的性能开销,尤其是在状态数据较大的情况下。
  3. 复杂性增加
    • 对于简单的场景,引入备忘录模式可能会增加代码的复杂性,显得“杀鸡用牛刀”。
  4. 状态暴露风险
    • 如果Memento对象的设计不当,可能会导致状态暴露给外部代码,破坏封装性。

备忘录模式的适用场景

  1. 撤销/重做功能
    • 需要实现撤销和重做功能的场景,如文本编辑器、绘图工具等。
  2. 事务管理
    • 需要保存和恢复对象状态的场景,如数据库事务管理、游戏存档等。
  3. 历史记录
    • 需要保存对象的历史状态以供后续使用的场景,如操作日志、版本控制等。
  4. 状态快照
    • 需要在某个时间点保存对象的状态,并在后续恢复的场景,如游戏中的保存点、虚拟机快照等。
  5. 复杂对象的状态管理
    • 对于状态结构复杂的对象,备忘录模式可以简化状态管理逻辑。