设计模式中,备忘录模式(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()方法生成一个Memento,restore()方法从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
- 用户操作 → 改变 editor 的内容。
editor.save()→ 生成一个快照。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,否则会丢失状态。

浙公网安备 33010602011771号