备忘录模式
简介
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
使用场景:
撤销/重做功能
事务回滚
游戏存档
浏览器历史记录
实现要点:
备忘录不可变(final)
窄接口和宽接口
保持封装性
UML
代码示例
一个文本编辑器
public class TextEditor {
/**
* 当前文本内容
*/
private String content;
/**
* 光标位置
*/
private int cursorPosition;
public TextEditor() {
this.content = "";
this.cursorPosition = 0;
}
/**
* 输入文本
*/
public void type(String text) {
// 在光标位置插入文本
String before = content.substring(0, cursorPosition);
String after = content.substring(cursorPosition);
content = before + text + after;
cursorPosition += text.length();
System.out.println("⌨️ 输入: \"" + text + "\"");
showStatus();
}
/**
* 删除文本
*/
public void delete(int count) {
if (cursorPosition >= count) {
String before = content.substring(0, cursorPosition - count);
String after = content.substring(cursorPosition);
content = before + after;
cursorPosition -= count;
System.out.println("⌫ 删除: " + count + " 个字符");
showStatus();
}
}
/**
* 移动光标
*/
public void moveCursor(int position) {
if (position >= 0 && position <= content.length()) {
cursorPosition = position;
System.out.println("👆 光标移动到位置: " + position);
}
}
/**
* 创建备忘录(保存当前状态)
*/
public TextMemento createMemento() {
System.out.println("💾 保存状态...");
return new TextMemento(content, cursorPosition);
}
/**
* 从备忘录恢复状态
*/
public void restoreFromMemento(TextMemento memento) {
this.content = memento.getContent();
this.cursorPosition = memento.getCursorPosition();
System.out.println("↩️ 恢复状态: " + memento.getInfo());
showStatus();
}
/**
* 显示当前状态
*/
public void showStatus() {
System.out.println(" 📄 内容: \"" + content + "\"");
System.out.println(" 📍 光标: " + cursorPosition);
}
/**
* 获取当前内容
*/
public String getContent() {
return content;
}
/**
* 获取光标位置
*/
public int getCursorPosition() {
return cursorPosition;
}
}文本编辑器的备忘录
public class TextMemento {
/**
* 保存的文本内容(状态)
*/
private final String content;
/**
* 光标位置(状态)
*/
private final int cursorPosition;
/**
* 创建时间
*/
private final String timestamp;
/**
* 构造函数
* 备忘录一旦创建,状态就不能再改变(final)
*/
public TextMemento(String content, int cursorPosition) {
this.content = content;
this.cursorPosition = cursorPosition;
this.timestamp = LocalDateTime.now().format(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
/**
* 获取保存的内容
* 只有原发器可以访问
*/
String getContent() {
return content;
}
/**
* 获取光标位置
*/
int getCursorPosition() {
return cursorPosition;
}
/**
* 获取时间戳
*/
public String getTimestamp() {
return timestamp;
}
/**
* 获取备忘录信息(不暴露详细内容)
*/
public String getInfo() {
int length = content.length();
String preview = length > 20 ? content.substring(0, 20) + "..." : content;
return String.format("[%s] 文本长度: %d, 预览: \"%s\"",
timestamp, length, preview);
}
}
历史管理器
public class History {
/**
* 撤销栈(保存历史状态)
*/
private Stack<TextMemento> undoStack;
/**
* 重做栈(保存被撤销的状态)
*/
private Stack<TextMemento> redoStack;
/**
* 最大历史记录数
*/
private final int maxHistorySize;
public History() {
this(50); // 默认保存50个历史记录
}
public History(int maxHistorySize) {
this.maxHistorySize = maxHistorySize;
this.undoStack = new Stack<>();
this.redoStack = new Stack<>();
}
/**
* 保存状态
*/
public void saveState(TextMemento memento) {
// 新的操作会清空重做栈
redoStack.clear();
// 如果超过最大历史数,移除最早的记录
if (undoStack.size() >= maxHistorySize) {
undoStack.remove(0);
}
undoStack.push(memento);
System.out.println("📚 历史记录已保存 (撤销栈: " + undoStack.size() + ")");
}
/**
* 撤销(返回上一个状态)
*/
public TextMemento undo(TextMemento currentState) {
if (canUndo()) {
// 保存当前状态到重做栈
redoStack.push(currentState);
// 从撤销栈取出上一个状态
TextMemento memento = undoStack.pop();
System.out.println("⬅️ 撤销 (撤销栈: " + undoStack.size() + ", 重做栈: " + redoStack.size() + ")");
return memento;
}
System.out.println("❌ 没有可撤销的操作");
return null;
}
/**
* 重做(返回下一个状态)
*/
public TextMemento redo(TextMemento currentState) {
if (canRedo()) {
// 保存当前状态到撤销栈
undoStack.push(currentState);
// 从重做栈取出下一个状态
TextMemento memento = redoStack.pop();
System.out.println("➡️ 重做 (撤销栈: " + undoStack.size() + ", 重做栈: " + redoStack.size() + ")");
return memento;
}
System.out.println("❌ 没有可重做的操作");
return null;
}
/**
* 是否可以撤销
*/
public boolean canUndo() {
return !undoStack.isEmpty();
}
/**
* 是否可以重做
*/
public boolean canRedo() {
return !redoStack.isEmpty();
}
/**
* 清空历史
*/
public void clear() {
undoStack.clear();
redoStack.clear();
System.out.println("🗑️ 历史记录已清空");
}
/**
* 显示历史记录
*/
public void showHistory() {
System.out.println("\n📜 历史记录:");
System.out.println("撤销栈 (" + undoStack.size() + " 条):");
for (int i = undoStack.size() - 1; i >= 0; i--) {
System.out.println(" " + (i + 1) + ". " + undoStack.get(i).getInfo());
}
if (!redoStack.isEmpty()) {
System.out.println("\n重做栈 (" + redoStack.size() + " 条):");
for (int i = redoStack.size() - 1; i >= 0; i--) {
System.out.println(" " + (i + 1) + ". " + redoStack.get(i).getInfo());
}
}
}
/**
* 获取历史记录数量
*/
public int getHistorySize() {
return undoStack.size();
}
}使用方式
public class Test {
public static void main(String[] args) {
System.out.println("=== 备忘录模式 - 文本编辑器示例 ===\n");
// 创建文本编辑器(原发器)
TextEditor editor = new TextEditor();
// 创建历史管理器(负责人)
History history = new History(10);
// ========== 场景1:基本编辑操作 ==========
System.out.println("【场景1:基本编辑操作】\n");
// 初始状态
history.saveState(editor.createMemento());
// 输入"Hello"
editor.type("Hello");
history.saveState(editor.createMemento());
// 输入" World"
editor.type(" World");
history.saveState(editor.createMemento());
// 输入"!"
editor.type("!");
history.saveState(editor.createMemento());
System.out.println("\n当前状态:");
editor.showStatus();
// ========== 场景2:撤销操作 ==========
System.out.println("\n\n【场景2:撤销操作】\n");
// 撤销一次
TextMemento memento = history.undo(editor.createMemento());
if (memento != null) {
editor.restoreFromMemento(memento);
}
// 再撤销一次
memento = history.undo(editor.createMemento());
if (memento != null) {
editor.restoreFromMemento(memento);
}
// ========== 场景3:重做操作 ==========
System.out.println("\n\n【场景3:重做操作】\n");
// 重做一次
memento = history.redo(editor.createMemento());
if (memento != null) {
editor.restoreFromMemento(memento);
}
// ========== 场景4:编辑后撤销栈清空 ==========
System.out.println("\n\n【场景4:新编辑操作会清空重做栈】\n");
// 进行新的编辑
editor.type("?");
history.saveState(editor.createMemento());
System.out.println("\n尝试重做:");
memento = history.redo(editor.createMemento());
// ========== 场景5:复杂编辑序列 ==========
System.out.println("\n\n【场景5:复杂编辑序列】\n");
// 清空编辑器
editor = new TextEditor();
history.clear();
// 保存初始状态
history.saveState(editor.createMemento());
// 编辑序列
String[] inputs = {"Java", " ", "Design", " ", "Patterns"};
for (String input : inputs) {
editor.type(input);
history.saveState(editor.createMemento());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("\n最终状态:");
editor.showStatus();
// 显示历史记录
history.showHistory();
// 连续撤销
System.out.println("\n\n连续撤销3次:");
for (int i = 0; i < 3; i++) {
memento = history.undo(editor.createMemento());
if (memento != null) {
editor.restoreFromMemento(memento);
}
}
// ========== 场景6:删除操作 ==========
System.out.println("\n\n【场景6:删除操作】\n");
editor.delete(3);
history.saveState(editor.createMemento());
// 撤销删除
System.out.println("\n撤销删除:");
memento = history.undo(editor.createMemento());
if (memento != null) {
editor.restoreFromMemento(memento);
}
// ========== 对比不使用备忘录模式 ==========
System.out.println("\n\n【对比:不使用备忘录模式】\n");
System.out.println("❌ 不使用备忘录模式:");
System.out.println(" - 需要手动记录每个状态变量");
System.out.println(" - 增加新状态变量时,所有保存/恢复代码都要修改");
System.out.println(" - 状态管理代码分散,难以维护");
System.out.println(" - 破坏封装性,外部需要知道所有状态细节");
System.out.println("\n✅ 使用备忘录模式:");
System.out.println(" - 自动保存完整状态");
System.out.println(" - 增加状态变量只需修改备忘录类");
System.out.println(" - 状态管理集中,易于维护");
System.out.println(" - 保持封装性,外部不需要知道状态细节");
// ========== 总结 ==========
System.out.println("\n\n=== 备忘录模式说明 ===");
System.out.println("1. 备忘录: TextMemento - 保存状态的快照");
System.out.println("2. 原发器: TextEditor - 创建和恢复备忘录");
System.out.println("3. 负责人: History - 管理备忘录(撤销/重做栈)");
System.out.println("4. 封装性: 备忘录状态对外不可见");
System.out.println("\n=== 备忘录模式优势 ===");
System.out.println("✅ 保存和恢复状态: 实现撤销/重做功能");
System.out.println("✅ 封装性: 不破坏对象的封装边界");
System.out.println("✅ 简化原发器: 状态管理逻辑分离");
System.out.println("✅ 状态独立: 每个备忘录独立存储");
System.out.println("\n=== 备忘录模式缺点 ===");
System.out.println("⚠️ 内存消耗: 频繁保存会占用大量内存");
System.out.println("⚠️ 性能开销: 创建备忘录需要复制状态");
System.out.println("⚠️ 生命周期管理: 需要管理备忘录的创建和销毁");
System.out.println("\n=== 备忘录模式应用场景 ===");
System.out.println("📌 撤销/重做: 文本编辑器、图形编辑器");
System.out.println("📌 事务回滚: 数据库事务、业务操作");
System.out.println("📌 游戏存档: 保存游戏进度");
System.out.println("📌 浏览器历史: 前进/后退功能");
System.out.println("📌 工作流: 流程状态保存和恢复");
System.out.println("📌 快照备份: 系统状态快照");
System.out.println("\n=== 备忘录模式关键点 ===");
System.out.println("🔑 备忘录不可变: 一旦创建不能修改(final)");
System.out.println("🔑 窄接口: 负责人只能传递备忘录,不能访问内容");
System.out.println("🔑 宽接口: 原发器可以访问备忘录的所有内容");
System.out.println("🔑 封装边界: 备忘录保护原发器的内部状态");
System.out.println("\n=== Java中的备忘录模式应用 ===");
System.out.println("🔸 Serializable: Java序列化机制");
System.out.println("🔸 Cloneable: 对象克隆");
System.out.println("🔸 JTextComponent: Swing的撤销/重做");
System.out.println("🔸 HttpSession: Web会话状态保存");
// 最终显示历史记录
System.out.println("\n\n最终历史记录:");
history.showHistory();
System.out.println("\n\n当前编辑器状态:");
editor.showStatus();
}
}Last updated
Was this helpful?