备忘录 设计模式是一种行为设计模式,可让您捕获和存储对象的内部状态,以便以后可以恢复它,而不会违反封装。
它在以下情况下特别有用:
您需要实现撤消/重做功能。
您希望支持 对象状态的检查点或版本控制。
您希望将状态存储的关注点 与状态管理逻辑分开。
让我们通过一个真实世界的示例,看看如何应用 备忘录 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. 封装被破坏
客户端必须手动获取和存储内部状态 () 才能实现撤消。getContent()
这暴露了实现细节,违反了信息隐藏的原则。 - 2. 手动快照管理
客户端负责管理内容的先前版本。
这使得撤消逻辑容易出错,并且与编辑器的内部结构紧密耦合。 - 3. 不可扩展
如果您想撤消多个作(而不仅仅是一个)怎么办?
或者恢复光标位置、格式和选择以及内容?
您需要扩展快照逻辑,将更多编辑器的内部状态泄漏到客户端中。
我们真正需要什么
我们需要一个允许:
TextEditor 公开捕获和恢复其状态的安全方法
客户端在不知道或接触内部字段的情况下管理这些状态(快照)
撤消/重做以可扩展、可维护和封装的方式实现
这正是 备忘录 Pattern 旨在解决的问题。
纪念品图案
备忘录 设计模式允许对象在不暴露其内部结构的情况下保存和恢复其状态。它通过将状态封装在称为 Meme备忘录对象中来实现这一点。
此模式非常适合实现撤消/重做、版本历史记录或状态回滚功能。
类图

- 1. 发起人(例如TextEditor)
要稍后捕获和还原其内部状态的对象。
它负责:
创建捕捉其当前状态的纪念品
在需要时从记忆中恢复其状态(例如,在撤消作期间)
它封装了决定保存什么状态的逻辑,确保客户端或看守者永远无法访问其内部结构。
- 2. 纪念品
一个被动对象,用于保存发起者在给定时间点的状态快照。
它不公开任何修改其状态的方法。
只有发起者才能访问纪念品内部的内部状态。 - 3. Concrete备忘录(可选)
在更复杂的系统中,备忘录 可能通过接口或抽象类定义,而 a 提供实际实现。ConcreteMeme备忘录
这允许将来的可扩展性或多种类型的纪念品。
在许多简单的用例(如我们的用例)中,备忘录 本身充当具体实现。 - 4. 看守人
看守人负责存储、管理和修复纪念品。
它从不检查或修改纪念品的内容,它只是将其视为一个黑匣子。
实施 备忘录
让我们使用 备忘录 Pattern 将我们幼稚的文本编辑器重构为干净、可维护的设计。
我们的目标是以以下方式启用撤消功能:
封装 – 编辑器的内部状态保持私密。
灵活 – 编辑器可以保存和恢复快照。
安全 – 客户端不直接作状态。
- 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;
}
}
这个类是被动的,不包含任何逻辑——只是编辑器状态的快照。
- 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 + "\"");
}
}
- 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
- 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
可扩展性:通过重做支持、多级撤消或持久版本控制轻松扩展
4152

被折叠的 条评论
为什么被折叠?



