备忘录设计模式

备忘录 设计模式是一种行为设计模式,可让您捕获和存储对象的内部状态,以便以后可以恢复它,而不会违反封装。
它在以下情况下特别有用:
您需要实现撤消/重做功能。
您希望支持 对象状态的检查点或版本控制。
您希望将状态存储的关注点 与状态管理逻辑分开。

让我们通过一个真实世界的示例,看看如何应用 备忘录 Pattern 来解决涉及在文本编辑器中实现撤消功能的实际问题。

问题:在文本编辑器中实现撤消
想象一下,您正在构建一个简单的文本编辑器。编辑器支持以下基本作:
type(String text)– 将文本附加到当前文档
getContent()– 返回当前文档文本
undo()– 恢复到以前版本的内容

实现打字和阅读很容易。真正的挑战是撤消。
朴素的实现:通过手动快照撤消
你可以从这样开始:

public class TextEditorNaive {
    private String content = "";

    public void type(String newText) {
        content += newText;
    }

    public void undo(String previousContent) {
        content = previousContent;
    }

    public String getContent() {
        return content;
    }    
}

用法示例

public class TextEditorUndoV1 {
    public static void main(String[] args) {
        TextEditorNaive editor = new TextEditorNaive();

        editor.type("Hello");
        String snapshot1 = editor.getContent(); // manual snapshot

        editor.type(" World");
        String snapshot2 = editor.getContent();

        System.out.println("Current Content: " + editor.getContent()); // Hello World

        // Undo 1 step
        editor.undo(snapshot1);
        System.out.println("After Undo: " + editor.getContent()); // Hello
    }
}

这个设计有什么问题?
虽然这种朴素的实现适用于非常基本的撤消逻辑,但它引入了几个主要问题:

  1. 1. 封装被破坏
    客户端必须手动获取和存储内部状态 () 才能实现撤消。getContent()
    这暴露了实现细节,违反了信息隐藏的原则。
  2. 2. 手动快照管理
    客户端负责管理内容的先前版本。
    这使得撤消逻辑容易出错,并且与编辑器的内部结构紧密耦合。
  3. 3. 不可扩展
    如果您想撤消多个作(而不仅仅是一个)怎么办?
    或者恢复光标位置、格式和选择以及内容?
    您需要扩展快照逻辑,将更多编辑器的内部状态泄漏到客户端中。

我们真正需要什么
我们需要一个允许:

TextEditor 公开捕获和恢复其状态的安全方法
客户端在不知道或接触内部字段的情况下管理这些状态(快照)
撤消/重做以可扩展、可维护和封装的方式实现
这正是 备忘录 Pattern 旨在解决的问题。

纪念品图案
备忘录 设计模式允许对象在不暴露其内部结构的情况下保存和恢复其状态。它通过将状态封装在称为 Meme备忘录对象中来实现这一点。
此模式非常适合实现撤消/重做、版本历史记录或状态回滚功能。

类图

 

  1. 1. 发起人(例如TextEditor)
    要稍后捕获和还原其内部状态的对象。

它负责:
创建捕捉其当前状态的纪念品
在需要时从记忆中恢复其状态(例如,在撤消作期间)
它封装了决定保存什么状态的逻辑,确保客户端或看守者永远无法访问其内部结构。

  1. 2. 纪念品
    一个被动对象,用于保存发起者在给定时间点的状态快照。
    它不公开任何修改其状态的方法。
    只有发起者才能访问纪念品内部的内部状态。
  2. 3. Concrete备忘录(可选)
    在更复杂的系统中,备忘录 可能通过接口或抽象类定义,而 a 提供实际实现。ConcreteMeme备忘录
    这允许将来的可扩展性或多种类型的纪念品。
    在许多简单的用例(如我们的用例)中,备忘录 本身充当具体实现。
  3. 4. 看守人
    看守人负责存储、管理和修复纪念品。
    它从不检查或修改纪念品的内容,它只是将其视为一个黑匣子。

实施 备忘录
让我们使用 备忘录 Pattern 将我们幼稚的文本编辑器重构为干净、可维护的设计。
我们的目标是以以下方式启用撤消功能:
封装 – 编辑器的内部状态保持私密。
灵活 – 编辑器可以保存和恢复快照。
安全 – 客户端不直接作状态。

  1. 1. 创建纪念品类 -TextEditor备忘录
    纪念品存储了内部状态的快照。通常是:TextEditor
    不可变 – 字段在 创建后不可修改。private final
    最小 – 它仅存储恢复所需的状态。
    封装 – 只有发起者 ( ) 应该知道如何使用它。
    TextEditor
public class TextEditorMemento {
    private final String state;

    public TextEditorMemento(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }    
}

这个类是被动的,不包含任何逻辑——只是编辑器状态的快照。

  1. 2. 创建发起人 –TextEditor
    发起方是我们要保存和恢复其状态的对象。它提供:

一种创建表示其当前状态的纪念品的方法。
一种 从给定纪念品中恢复其状态的方法。

public class TextEditor {
    private String content = "";

    public void type(String newText) {
        content += newText;
        System.out.println("Typed: " + newText);
    }

    public String getContent() {
        return content;
    }

    public TextEditorMemento save() {
        System.out.println("Saving state: \"" + content + "\"");
        return new TextEditorMemento(content);
    }

    public void restore(TextEditorMemento memento) {
        content = memento.getState();
        System.out.println("Restored state to: \"" + content + "\"");
    }
}
  1. 3. 创建看守人 –TextEditorUndoManager
    此类管理撤消堆栈。它负责:
    管理国家历史(纪念品)。
    在正确的时间调用发起方的 和方法。save()restore()
    不检查或修改内部状态本身。
import java.util.Stack;

public class TextEditorUndoManager {
    private final Stack<TextEditorMemento> history = new Stack<>();

    public void save(TextEditor editor) {
        history.push(editor.save());
    }

    public void undo(TextEditor editor) {
        if (!history.isEmpty()) {
            editor.restore(history.pop());
        } else {
            System.out.println("Nothing to undo.");
        }
    }
}

这 允许我们干净地执行撤消作,而无需客户端直接管理快照。TextEditorUndoManager

  1. 4. 客户端代码 – 使用具有撤消支持的编辑器
public class TextEditorUndoV2 {
    public static void main(String[] args) {
        TextEditor editor = new TextEditor();
        TextEditorUndoManager undoManager = new TextEditorUndoManager();

        editor.type("Hello");
        undoManager.save(editor);  // save state: Hello

        editor.type(" World");
        undoManager.save(editor);  // save state: Hello World

        editor.type("!");
        System.out.println("Current Content: " + editor.getContent()); // Hello World!

        System.out.println("\n--- Undo 1 ---");
        undoManager.undo(editor); // Back to: Hello World

        System.out.println("\n--- Undo 2 ---");
        undoManager.undo(editor); // Back to: Hello

        System.out.println("\n--- Undo 3 ---");
        undoManager.undo(editor); // Nothing left to undo
    }
}

输出

Typed: Hello
Saving state: "Hello"
Typed:  World
Saving state: "Hello World"
Typed: !
Current Content: Hello World!

--- Undo 1 ---
Restored state to: "Hello World"

--- Undo 2 ---
Restored state to: "Hello"

--- Undo 3 ---
Nothing to undo.

我们取得了什么成就
封装:编辑器的内部状态永远不会直接暴露给客户端
干净的撤消逻辑:客户端不需要管理或解释状态,只需保存和恢复
关注点分离: 句柄状态和 句柄历史记录TextEditorTextEditorUndoManager
可扩展性:通过重做支持、多级撤消或持久版本控制轻松扩展

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值