设计模式中,备忘录模式(Memento Pattern)实现与原理一文详解!

备忘录模式(Memento Pattern)实现与详解

✅ 完整代码示例(Memento 模式实现 TextEditor 的 Undo/Redo)

// 备忘录:负责存储 TextEditor 的状态(不可变)
class Memento {
    private final String state;

    public Memento(String state) {
        this.state = state;  // 保存传入的状态
    }

    public String getState() {
        return state;  // 仅提供读取方法,不允许外部修改
    }
}

// Originator:需要被保存和恢复状态的对象
class TextEditor {
    private StringBuilder buffer = new StringBuilder();

    public void add(char ch) {
        buffer.append(ch);
    }

    public void add(String s) {
        buffer.append(s);
    }

    public void delete() {
        if (buffer.length() > 0) {
            buffer.deleteCharAt(buffer.length() - 1);
        }
    }

    public String getText() {
        return buffer.toString();
    }

    // 创建一个备忘录
    public Memento save() {
        return new Memento(buffer.toString());
    }

    // 从备忘录恢复
    public void restore(Memento m) {
        buffer.delete(0, buffer.length());
        buffer.append(m.getState());
    }
}

// Caretaker:负责保存和管理多个备忘录(用于支持 Undo / Redo)
import java.util.Stack;

class History {
    private Stack<Memento> undoStack = new Stack<>();
    private Stack<Memento> redoStack = new Stack<>();

    public void save(Memento m) {
        undoStack.push(m);
        redoStack.clear();  // 每次新操作后,清空 redo 记录
    }

    public Memento undo(Memento current) {
        if (!undoStack.isEmpty()) {
            redoStack.push(current);  // 先把当前状态保存到 redo
            return undoStack.pop();   // 返回上一个状态
        }
        return current;
    }

    public Memento redo(Memento current) {
        if (!redoStack.isEmpty()) {
            undoStack.push(current);  // redo 前先把当前状态保存
            return redoStack.pop();   // 返回下一个状态
        }
        return current;
    }
}

// 测试代码
public class MementoDemo {
    public static void main(String[] args) {
        TextEditor editor = new TextEditor();
        History history = new History();

        // 初始输入
        editor.add("Hello");
        history.save(editor.save()); // 保存状态1
        System.out.println("输入: " + editor.getText());

        editor.add(" World");
        history.save(editor.save()); // 保存状态2
        System.out.println("输入: " + editor.getText());

        editor.add("!!!");
        history.save(editor.save()); // 保存状态3
        System.out.println("输入: " + editor.getText());

        // 撤销 (Undo)
        editor.restore(history.undo(editor.save()));
        System.out.println("撤销后: " + editor.getText());

        editor.restore(history.undo(editor.save()));
        System.out.println("再撤销: " + editor.getText());

        // 重做 (Redo)
        editor.restore(history.redo(editor.save()));
        System.out.println("重做后: " + editor.getText());
    }
}

📖 逐行解释(通俗易懂版)

1. Memento 角色

class Memento {
    private final String state;
    ...
}
  • Memento 就是一个“快照”,保存 TextEditor 的状态
  • final 关键字保证状态 不可修改,防止外部随意篡改历史记录。

2. Originator 角色

class TextEditor {
    private StringBuilder buffer = new StringBuilder();
    ...
}
  • 这是 原始对象,用户对它进行操作(增、删字符)。
  • 提供了 save() 方法生成一个 Mementorestore() 方法从 Memento 恢复。
  • 关键思想:对象自己知道如何保存和恢复状态

3. Caretaker 角色

class History {
    private Stack<Memento> undoStack = new Stack<>();
    private Stack<Memento> redoStack = new Stack<>();
    ...
}
  • History看护者,只负责存放快照,但不理解快照内容。
  • 使用 undoStack 保存历史状态,使用 redoStack 实现重做功能。
  • 这里用 栈 (Stack),因为撤销/重做就是“后进先出”的逻辑。

4. 运行流程

editor.add("Hello");
history.save(editor.save()); // 保存状态1
  1. 用户操作 → 改变 editor 的内容。
  2. editor.save() → 生成一个快照。
  3. history.save(memento) → 把快照放进历史栈。

这样,每一次操作都会留下“检查点”,方便以后回滚。


5. Undo 的难点

public Memento undo(Memento current) {
    redoStack.push(current);
    return undoStack.pop();
}
  • 关键点:撤销时,不能只回退,还要把“当前状态”放进 redoStack。
  • 否则用户想要“重做”就找不到刚刚撤销掉的状态了。

6. Redo 的难点

public Memento redo(Memento current) {
    undoStack.push(current);
    return redoStack.pop();
}
  • 关键点:redo 时也要先把当前状态压回 undoStack。
  • 这样用户可以无限来回切换 “撤销 ←→ 重做”。

📝 输出示例

运行上面代码会输出:

输入: Hello
输入: Hello World
输入: Hello World!!!
撤销后: Hello World
再撤销: Hello
重做后: Hello World

👉 总结

  • Memento 保存状态(只读)。
  • TextEditor (Originator) 负责生成/恢复 Memento。
  • History (Caretaker) 管理多个快照,实现 Undo/Redo。
  • 难点是 undo/redo 栈的维护:撤销时要存 redo,重做时要存 undo,否则会丢失状态。
posted @ 2025-09-08 22:15  AlphaGeek  阅读(12)  评论(0)    收藏  举报