设计模式-16备忘录模式Memento Pattern
·
备忘录模式 (Memento Pattern)
概述 (Overview)
备忘录模式是一种行为型设计模式,它允许在不暴露对象实现细节的情况下捕获对象的内部状态,并在以后将该对象恢复到原先保存的状态。
Memento Pattern is a behavioral design pattern that lets you save and restore the previous state of an object without revealing the details of its implementation.
意图 (Intent)
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
Without violating encapsulation, capture and externalize an object’s internal state so that the object can be restored to this state later.
适用场景 (When to Use)
- 当需要保存和恢复对象的状态,而又不破坏对象的封装性时
- 当需要实现撤销/重做功能时
- 当需要保存对象的历史状态以便回滚时
- 当需要创建对象状态的检查点时
- 当需要实现事务回滚机制时
- 当需要保存对象的多个状态快照时
结构 (Structure)
基础备忘录模式结构图 (Basic Memento Pattern Structure)
实现方式 (Implementation Approaches)
1. 经典备忘录模式 (Classic Memento Pattern)
// 备忘录接口 - Memento Interface
public interface Memento {
// 备忘录接口可以为空,也可以定义一些获取状态的方法
}
// 具体备忘录 - Concrete Memento
public class TextEditorMemento implements Memento {
private final String content;
private final int cursorPosition;
private final Date timestamp;
public TextEditorMemento(String content, int cursorPosition) {
this.content = content;
this.cursorPosition = cursorPosition;
this.timestamp = new Date();
}
public String getContent() {
return content;
}
public int getCursorPosition() {
return cursorPosition;
}
public Date getTimestamp() {
return timestamp;
}
@Override
public String toString() {
return String.format("Memento{content='%s...', cursor=%d, time=%s}",
content.substring(0, Math.min(content.length(), 20)),
cursorPosition, timestamp);
}
}
// 原发器 - Originator
public class TextEditor {
private String content;
private int cursorPosition;
public TextEditor() {
this.content = "";
this.cursorPosition = 0;
}
public void write(String text) {
String beforeCursor = content.substring(0, cursorPosition);
String afterCursor = content.substring(cursorPosition);
content = beforeCursor + text + afterCursor;
cursorPosition += text.length();
System.out.println("写入文本: '" + text + "'");
}
public void delete(int length) {
if (length <= 0 || cursorPosition < length) return;
String beforeCursor = content.substring(0, cursorPosition - length);
String afterCursor = content.substring(cursorPosition);
content = beforeCursor + afterCursor;
cursorPosition -= length;
System.out.println("删除 " + length + " 个字符");
}
public void moveCursor(int position) {
if (position >= 0 && position <= content.length()) {
cursorPosition = position;
System.out.println("移动光标到位置: " + position);
}
}
public void moveCursorToEnd() {
cursorPosition = content.length();
System.out.println("移动光标到末尾");
}
// 创建备忘录
public TextEditorMemento createMemento() {
System.out.println("创建备忘录: " + content.substring(0, Math.min(content.length(), 10)) + "...");
return new TextEditorMemento(content, cursorPosition);
}
// 从备忘录恢复状态
public void restoreFromMemento(TextEditorMemento memento) {
this.content = memento.getContent();
this.cursorPosition = memento.getCursorPosition();
System.out.println("从备忘录恢复状态");
}
// 获取当前状态
public String getContent() {
return content;
}
public int getCursorPosition() {
return cursorPosition;
}
public void display() {
System.out.println("当前内容: \"" + content + "\"");
System.out.println("光标位置: " + cursorPosition);
if (cursorPosition > 0) {
System.out.println("光标指示: " + " ".repeat(cursorPosition - 1) + "^");
}
}
}
// 管理者 - Caretaker
public class TextEditorHistory {
private final Stack<TextEditorMemento> history;
private final int maxSize;
public TextEditorHistory() {
this(50); // 默认最多保存50个状态
}
public TextEditorHistory(int maxSize) {
this.history = new Stack<>();
this.maxSize = maxSize;
}
public void save(TextEditorMemento memento) {
if (history.size() >= maxSize) {
history.remove(0); // 移除最旧的状态
}
history.push(memento);
System.out.println("保存备忘录到历史记录");
}
public TextEditorMemento undo() {
if (history.isEmpty()) {
throw new IllegalStateException("没有可撤销的状态");
}
TextEditorMemento memento = history.pop();
System.out.println("撤销到备忘录: " + memento);
return memento;
}
public boolean canUndo() {
return !history.isEmpty();
}
public void clear() {
history.clear();
System.out.println("清空历史记录");
}
public int getHistorySize() {
return history.size();
}
public TextEditorMemento getMemento(int index) {
if (index < 0 || index >= history.size()) {
throw new IndexOutOfBoundsException("索引超出范围");
}
return history.get(index);
}
}
// 客户端使用 - Client Usage
public class TextEditorDemo {
public static void main(String[] args) {
System.out.println("=== 文本编辑器备忘录模式演示 ===");
TextEditor editor = new TextEditor();
TextEditorHistory history = new TextEditorHistory();
System.out.println("\n--- 初始状态 ---");
editor.display();
// 编辑文本并保存状态
System.out.println("\n--- 编辑文本 ---");
editor.write("Hello ");
history.save(editor.createMemento());
editor.write("World");
history.save(editor.createMemento());
editor.write("!");
history.save(editor.createMemento());
editor.display();
// 继续编辑
System.out.println("\n--- 继续编辑 ---");
editor.write(" Welcome");
editor.display();
// 撤销操作
System.out.println("\n--- 撤销操作 ---");
if (history.canUndo()) {
editor.restoreFromMemento(history.undo());
editor.display();
}
if (history.canUndo()) {
editor.restoreFromMemento(history.undo());
editor.display();
}
// 再次编辑
System.out.println("\n--- 再次编辑 ---");
editor.write(" Java");
history.save(editor.createMemento());
editor.display();
System.out.println("\n历史记录大小: " + history.getHistorySize());
}
}
2. 宽接口备忘录模式 (Wide Interface Memento Pattern)
// 宽接口备忘录 - Wide Interface Memento
public class GameMemento {
private final int level;
private final int score;
private final int health;
private final double x;
private final double y;
private final String weapon;
private final List<String> inventory;
private final Date timestamp;
public GameMemento(int level, int score, int health, double x, double y,
String weapon, List<String> inventory) {
this.level = level;
this.score = score;
this.health = health;
this.x = x;
this.y = y;
this.weapon = weapon;
this.inventory = new ArrayList<>(inventory);
this.timestamp = new Date();
}
// 宽接口:提供所有状态的访问方法
public int getLevel() { return level; }
public int getScore() { return score; }
public int getHealth() { return health; }
public double getX() { return x; }
public double getY() { return y; }
public String getWeapon() { return weapon; }
public List<String> getInventory() { return new ArrayList<>(inventory); }
public Date getTimestamp() { return timestamp; }
@Override
public String toString() {
return String.format("GameMemento{level=%d, score=%d, health=%d, pos=(%.1f,%.1f), weapon='%s', items=%d}",
level, score, health, x, y, weapon, inventory.size());
}
}
// 游戏角色 - Game Character (Originator)
public class GameCharacter {
private int level;
private int score;
private int health;
private double x;
private double y;
private String weapon;
private List<String> inventory;
public GameCharacter(String name) {
this.level = 1;
this.score = 0;
this.health = 100;
this.x = 0.0;
this.y = 0.0;
this.weapon = "空手";
this.inventory = new ArrayList<>();
this.inventory.add("地图");
}
public void move(double dx, double dy) {
this.x += dx;
this.y += dy;
System.out.printf("移动到 (%.1f, %.1f)%n", x, y);
}
public void attack() {
if (health > 0) {
System.out.println("使用 " + weapon + " 攻击!");
score += 10;
} else {
System.out.println("生命值不足,无法攻击");
}
}
public void takeDamage(int damage) {
health = Math.max(0, health - damage);
System.out.println("受到 " + damage + " 点伤害,当前生命值: " + health);
}
public void pickUpItem(String item) {
inventory.add(item);
System.out.println("拾取物品: " + item);
}
public void changeWeapon(String newWeapon) {
String oldWeapon = weapon;
weapon = newWeapon;
System.out.println("更换武器: " + oldWeapon + " -> " + newWeapon);
}
public void levelUp() {
level++;
health = 100; // 升级后恢复生命值
System.out.println("升级!当前等级: " + level);
}
// 创建备忘录
public GameMemento createSavePoint() {
System.out.println("创建游戏存档点...");
return new GameMemento(level, score, health, x, y, weapon, inventory);
}
// 从备忘录恢复
public void restoreFromSavePoint(GameMemento memento) {
this.level = memento.getLevel();
this.score = memento.getScore();
this.health = memento.getHealth();
this.x = memento.getX();
this.y = memento.getY();
this.weapon = memento.getWeapon();
this.inventory = new ArrayList<>(memento.getInventory());
System.out.println("从存档点恢复游戏状态");
}
public void displayStatus() {
System.out.println("\n=== 角色状态 ===");
System.out.println("等级: " + level);
System.out.println("分数: " + score);
System.out.println("生命值: " + health);
System.out.println("位置: (" + x + ", " + y + ")");
System.out.println("武器: " + weapon);
System.out.println("背包: " + inventory);
}
}
// 游戏存档管理器 - Game Save Manager (Caretaker)
public class GameSaveManager {
private final Map<String, GameMemento> saveSlots;
private final int maxSlots;
private String autoSaveSlot;
public GameSaveManager(int maxSlots) {
this.saveSlots = new LinkedHashMap<>();
this.maxSlots = maxSlots;
this.autoSaveSlot = "auto_save";
}
public void saveGame(String slotName, GameMemento memento) {
if (saveSlots.size() >= maxSlots && !saveSlots.containsKey(slotName)) {
// 移除最旧的存档
String oldestSlot = saveSlots.keySet().iterator().next();
saveSlots.remove(oldestSlot);
System.out.println("存档槽已满,移除最旧的存档: " + oldestSlot);
}
saveSlots.put(slotName, memento);
System.out.println("游戏已保存到槽位: " + slotName);
}
public GameMemento loadGame(String slotName) {
GameMemento memento = saveSlots.get(slotName);
if (memento == null) {
throw new IllegalArgumentException("存档槽不存在: " + slotName);
}
System.out.println("从槽位加载游戏: " + slotName);
return memento;
}
public void autoSave(GameMemento memento) {
saveGame(autoSaveSlot, memento);
System.out.println("自动保存完成");
}
public GameMemento loadAutoSave() {
return loadGame(autoSaveSlot);
}
public boolean hasAutoSave() {
return saveSlots.containsKey(autoSaveSlot);
}
public void deleteSave(String slotName) {
if (saveSlots.remove(slotName) != null) {
System.out.println("删除存档: " + slotName);
}
}
public List<String> getSaveSlots() {
return new ArrayList<>(saveSlots.keySet());
}
public void displaySaveInfo() {
System.out.println("\n=== 存档信息 ===");
if (saveSlots.isEmpty()) {
System.out.println("没有存档");
return;
}
for (Map.Entry<String, GameMemento> entry : saveSlots.entrySet()) {
System.out.println("槽位: " + entry.getKey() + " -> " + entry.getValue());
}
}
}
// 客户端使用 - Client Usage
public class GameSaveDemo {
public static void main(String[] args) {
System.out.println("=== 游戏存档备忘录模式演示 ===");
GameCharacter character = new GameCharacter("勇者");
GameSaveManager saveManager = new GameSaveManager(5);
// 初始状态
character.displayStatus();
// 游戏过程
System.out.println("\n--- 开始游戏 ---");
character.move(10, 5);
character.pickUpItem("生命药水");
character.attack();
saveManager.saveGame("checkpoint_1", character.createSavePoint());
character.move(20, 10);
character.takeDamage(30);
character.pickUpItem("铁剑");
character.changeWeapon("铁剑");
character.attack();
saveManager.saveGame("checkpoint_2", character.createSavePoint());
character.move(30, 15);
character.pickUpItem("魔法卷轴");
character.attack();
character.levelUp();
saveManager.autoSave(character.createSavePoint());
// 遭遇失败
System.out.println("\n--- 遭遇强敌,角色死亡 ---");
character.takeDamage(80);
character.displayStatus();
// 从存档恢复
System.out.println("\n--- 从存档点恢复 ---");
character.restoreFromSavePoint(saveManager.loadGame("checkpoint_2"));
character.displayStatus();
// 继续游戏
System.out.println("\n--- 继续游戏 ---");
character.move(25, 12);
character.pickUpItem("盾牌");
character.attack();
saveManager.saveGame("checkpoint_3", character.createSavePoint());
// 显示存档信息
saveManager.displaySaveInfo();
System.out.println("\n游戏继续...");
}
}
3. 增量备忘录模式 (Incremental Memento Pattern)
// 增量状态变化 - Incremental State Change
public class StateChange {
private final String propertyName;
private final Object oldValue;
private final Object newValue;
private final Date timestamp;
public StateChange(String propertyName, Object oldValue, Object newValue) {
this.propertyName = propertyName;
this.oldValue = oldValue;
this.newValue = newValue;
this.timestamp = new Date();
}
public String getPropertyName() { return propertyName; }
public Object getOldValue() { return oldValue; }
public Object getNewValue() { return newValue; }
public Date getTimestamp() { return timestamp; }
@Override
public String toString() {
return String.format("Change{%s: %s -> %s}", propertyName, oldValue, newValue);
}
}
// 增量备忘录 - Incremental Memento
public class DocumentMemento {
private final List<StateChange> changes;
private final Date timestamp;
public DocumentMemento() {
this.changes = new ArrayList<>();
this.timestamp = new Date();
}
public void addChange(StateChange change) {
changes.add(change);
}
public List<StateChange> getChanges() {
return new ArrayList<>(changes);
}
public Date getTimestamp() {
return timestamp;
}
public boolean isEmpty() {
return changes.isEmpty();
}
@Override
public String toString() {
return String.format("DocumentMemento{changes=%d, time=%s}", changes.size(), timestamp);
}
}
// 文档编辑器 - Document Editor (Originator)
public class DocumentEditor {
private String title;
private String content;
private String font;
private int fontSize;
private String color;
private boolean bold;
private boolean italic;
private boolean underline;
public DocumentEditor() {
this.title = "Untitled";
this.content = "";
this.font = "Arial";
this.fontSize = 12;
this.color = "Black";
this.bold = false;
this.italic = false;
this.underline = false;
}
public void setTitle(String title) {
String oldTitle = this.title;
this.title = title;
System.out.println("设置标题: " + title);
// 这里可以记录状态变化
}
public void setContent(String content) {
String oldContent = this.content;
this.content = content;
System.out.println("设置内容: " + content.substring(0, Math.min(content.length(), 20)) + "...");
// 这里可以记录状态变化
}
public void setFont(String font) {
String oldFont = this.font;
this.font = font;
System.out.println("设置字体: " + font);
// 这里可以记录状态变化
}
public void setFontSize(int fontSize) {
int oldSize = this.fontSize;
this.fontSize = fontSize;
System.out.println("设置字体大小: " + fontSize);
// 这里可以记录状态变化
}
public void setColor(String color) {
String oldColor = this.color;
this.color = color;
System.out.println("设置颜色: " + color);
// 这里可以记录状态变化
}
public void toggleBold() {
this.bold = !bold;
System.out.println("切换粗体: " + bold);
// 这里可以记录状态变化
}
public void toggleItalic() {
this.italic = !italic;
System.out.println("切换斜体: " + italic);
// 这里可以记录状态变化
}
public void toggleUnderline() {
this.underline = !underline;
System.out.println("切换下划线: " + underline);
// 这里可以记录状态变化
}
// 创建增量备忘录
public DocumentMemento createIncrementalMemento() {
DocumentMemento memento = new DocumentMemento();
// 在实际应用中,这里会记录自上次备忘录以来的所有变化
memento.addChange(new StateChange("title", null, title));
memento.addChange(new StateChange("content", null, content));
memento.addChange(new StateChange("font", null, font));
memento.addChange(new StateChange("fontSize", null, fontSize));
memento.addChange(new StateChange("color", null, color));
memento.addChange(new StateChange("bold", null, bold));
memento.addChange(new StateChange("italic", null, italic));
memento.addChange(new StateChange("underline", null, underline));
System.out.println("创建增量备忘录");
return memento;
}
// 从增量备忘录恢复
public void restoreFromIncrementalMemento(DocumentMemento memento) {
System.out.println("从增量备忘录恢复:");
for (StateChange change : memento.getChanges()) {
System.out.println(" " + change);
// 在实际应用中,这里会根据变化类型恢复相应的状态
}
System.out.println("恢复完成");
}
// 创建完整备忘录(快照)
public DocumentMemento createFullMemento() {
return createIncrementalMemento(); // 在实际应用中会有所不同
}
public void displayDocumentInfo() {
System.out.println("\n=== 文档信息 ===");
System.out.println("标题: " + title);
System.out.println("内容: " + content.substring(0, Math.min(content.length(), 30)) + "...");
System.out.println("字体: " + font + ", 大小: " + fontSize + ", 颜色: " + color);
System.out.println("格式: " + (bold ? "粗体" : "") + " " + (italic ? "斜体" : "") + " " + (underline ? "下划线" : ""));
}
}
// 文档历史管理器 - Document History Manager (Caretaker)
public class DocumentHistoryManager {
private final Stack<DocumentMemento> undoStack;
private final Stack<DocumentMemento> redoStack;
private final int maxUndoLevels;
public DocumentHistoryManager() {
this(20); // 默认支持20级撤销
}
public DocumentHistoryManager(int maxUndoLevels) {
this.undoStack = new Stack<>();
this.redoStack = new Stack<>();
this.maxUndoLevels = maxUndoLevels;
}
public void saveState(DocumentMemento memento) {
if (undoStack.size() >= maxUndoLevels) {
undoStack.remove(0); // 移除最旧的状态
}
undoStack.push(memento);
redoStack.clear(); // 保存新状态后清空重做栈
System.out.println("保存文档状态");
}
public DocumentMemento undo() {
if (undoStack.isEmpty()) {
throw new IllegalStateException("没有可撤销的状态");
}
DocumentMemento current = undoStack.pop();
redoStack.push(current);
if (undoStack.isEmpty()) {
throw new IllegalStateException("已经是最初状态");
}
DocumentMemento previous = undoStack.peek();
System.out.println("执行撤销操作");
return previous;
}
public DocumentMemento redo() {
if (redoStack.isEmpty()) {
throw new IllegalStateException("没有可重做的状态");
}
DocumentMemento memento = redoStack.pop();
undoStack.push(memento);
System.out.println("执行重做操作");
return memento;
}
public boolean canUndo() {
return undoStack.size() > 1; // 至少有一个状态可以撤销到
}
public boolean canRedo() {
return !redoStack.isEmpty();
}
public void clearHistory() {
undoStack.clear();
redoStack.clear();
System.out.println("清空历史记录");
}
public int getUndoStackSize() {
return undoStack.size();
}
public int getRedoStackSize() {
return redoStack.size();
}
}
// 客户端使用 - Client Usage
public class IncrementalMementoDemo {
public static void main(String[] args) {
System.out.println("=== 增量备忘录模式演示 ===");
DocumentEditor editor = new DocumentEditor();
DocumentHistoryManager history = new DocumentHistoryManager();
// 初始状态
editor.displayDocumentInfo();
history.saveState(editor.createFullMemento());
// 编辑文档
System.out.println("\n--- 编辑文档 ---");
editor.setTitle("我的文档");
editor.setContent("这是一个测试文档,用于演示备忘录模式。");
editor.setFont("Times New Roman");
editor.setFontSize(14);
editor.toggleBold();
history.saveState(editor.createIncrementalMemento());
// 继续编辑
System.out.println("\n--- 继续编辑 ---");
editor.setColor("Blue");
editor.toggleItalic();
editor.setContent("这是一个测试文档,用于演示备忘录模式。现在内容更丰富了。");
history.saveState(editor.createIncrementalMemento());
editor.displayDocumentInfo();
// 撤销操作
System.out.println("\n--- 撤销操作 ---");
if (history.canUndo()) {
editor.restoreFromIncrementalMemento(history.undo());
editor.displayDocumentInfo();
}
// 重做操作
System.out.println("\n--- 重做操作 ---");
if (history.canRedo()) {
editor.restoreFromIncrementalMemento(history.redo());
editor.displayDocumentInfo();
}
System.out.println("\n撤销栈大小: " + history.getUndoStackSize());
System.out.println("重做栈大小: " + history.getRedoStackSize());
}
}
框架源码中的应用 (Framework Applications)
1. Java Serializable - 序列化机制
// Java序列化中的备忘录模式应用
// Java Serialization Memento Pattern Application
// 可序列化的游戏状态 - Serializable Game State
public class GameState implements Serializable {
private static final long serialVersionUID = 1L;
private String playerName;
private int level;
private int score;
private double playerX;
private double playerY;
private List<String> inventory;
private Map<String, Integer> achievements;
public GameState(String playerName) {
this.playerName = playerName;
this.level = 1;
this.score = 0;
this.playerX = 0.0;
this.playerY = 0.0;
this.inventory = new ArrayList<>();
this.achievements = new HashMap<>();
}
// 游戏操作方法
public void movePlayer(double dx, double dy) {
playerX += dx;
playerY += dy;
System.out.printf("玩家移动到 (%.1f, %.1f)%n", playerX, playerY);
}
public void addScore(int points) {
score += points;
System.out.println("获得分数: " + points + ", 总分数: " + score);
}
public void levelUp() {
level++;
System.out.println("升级!当前等级: " + level);
}
public void addItem(String item) {
inventory.add(item);
System.out.println("获得物品: " + item);
}
public void unlockAchievement(String achievement, int points) {
achievements.put(achievement, points);
addScore(points);
System.out.println("解锁成就: " + achievement);
}
// 创建备忘录(序列化)
public byte[] createMemento() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
oos.close();
System.out.println("创建游戏状态备忘录");
return baos.toByteArray();
}
// 从备忘录恢复(反序列化)
public static GameState restoreFromMemento(byte[] mementoData) throws IOException, ClassNotFoundException {
ByteArrayInputStream bais = new ByteArrayInputStream(mementoData);
ObjectInputStream ois = new ObjectInputStream(bais);
GameState state = (GameState) ois.readObject();
ois.close();
System.out.println("从备忘录恢复游戏状态");
return state;
}
public void displayStatus() {
System.out.println("\n=== 游戏状态 ===");
System.out.println("玩家: " + playerName);
System.out.println("等级: " + level);
System.out.println("分数: " + score);
System.out.println("位置: (" + playerX + ", " + playerY + ")");
System.out.println("背包: " + inventory);
System.out.println("成就: " + achievements);
}
@Override
public String toString() {
return String.format("GameState{%s, Lv.%d, Score:%d, (%.1f,%.1f), Items:%d}",
playerName, level, score, playerX, playerY, inventory.size());
}
}
// 游戏存档管理器 - Game Save Manager (Caretaker)
public class SerializableGameSaveManager {
private final Map<String, byte[]> saveSlots;
private final String saveDirectory;
public SerializableGameSaveManager(String saveDirectory) {
this.saveSlots = new LinkedHashMap<>();
this.saveDirectory = saveDirectory;
ensureSaveDirectoryExists();
}
private void ensureSaveDirectoryExists() {
File dir = new File(saveDirectory);
if (!dir.exists()) {
dir.mkdirs();
}
}
public void saveGame(String slotName, GameState gameState) throws IOException {
byte[] mementoData = gameState.createMemento();
saveSlots.put(slotName, mementoData);
// 同时保存到文件
String fileName = saveDirectory + File.separator + slotName + ".sav";
try (FileOutputStream fos = new FileOutputStream(fileName)) {
fos.write(mementoData);
}
System.out.println("游戏已保存到槽位: " + slotName);
}
public GameState loadGame(String slotName) throws IOException, ClassNotFoundException {
byte[] mementoData = saveSlots.get(slotName);
if (mementoData == null) {
// 尝试从文件加载
String fileName = saveDirectory + File.separator + slotName + ".sav";
File file = new File(fileName);
if (file.exists()) {
mementoData = Files.readAllBytes(file.toPath());
saveSlots.put(slotName, mementoData);
} else {
throw new IllegalArgumentException("存档不存在: " + slotName);
}
}
System.out.println("从槽位加载游戏: " + slotName);
return GameState.restoreFromMemento(mementoData);
}
public void autoSave(GameState gameState) throws IOException {
String autoSaveName = "auto_save_" + System.currentTimeMillis();
saveGame(autoSaveName, gameState);
System.out.println("自动保存完成: " + autoSaveName);
}
public List<String> getSaveSlots() {
return new ArrayList<>(saveSlots.keySet());
}
public void deleteSave(String slotName) {
saveSlots.remove(slotName);
// 同时删除文件
String fileName = saveDirectory + File.separator + slotName + ".sav";
File file = new File(fileName);
if (file.exists()) {
file.delete();
}
System.out.println("删除存档: " + slotName);
}
}
// 客户端使用 - Client Usage
public class SerializableMementoDemo {
public static void main(String[] args) {
System.out.println("=== Java序列化备忘录模式演示 ===");
try {
GameState game = new GameState("勇者");
SerializableGameSaveManager saveManager = new SerializableGameSaveManager("saves");
// 初始状态
game.displayStatus();
// 游戏过程
System.out.println("\n--- 开始游戏 ---");
game.movePlayer(10, 5);
game.addScore(100);
game.addItem("生命药水");
game.unlockAchievement("首次移动", 50);
saveManager.saveGame("checkpoint_1", game);
game.movePlayer(20, 10);
game.addScore(200);
game.levelUp();
game.addItem("铁剑");
game.unlockAchievement("首次升级", 100);
saveManager.saveGame("checkpoint_2", game);
// 创建游戏副本进行实验
System.out.println("\n--- 创建游戏副本进行实验 ---");
GameState experimentalGame = GameState.restoreFromMemento(game.createMemento());
experimentalGame.displayStatus();
// 在副本中进行危险操作
System.out.println("\n--- 在副本中进行危险操作 ---");
experimentalGame.movePlayer(100, 50);
experimentalGame.addScore(500);
experimentalGame.takeDamage(80);
experimentalGame.displayStatus();
// 实验失败,恢复原状态
System.out.println("\n--- 实验失败,恢复原状态 ---");
game.displayStatus();
// 继续正常游戏
System.out.println("\n--- 继续正常游戏 ---");
game.movePlayer(15, 8);
game.addScore(150);
saveManager.autoSave(game);
game.displayStatus();
// 显示存档信息
System.out.println("\n可用存档:");
saveManager.getSaveSlots().forEach(System.out::println);
} catch (Exception e) {
System.err.println("游戏存档操作失败: " + e.getMessage());
e.printStackTrace();
}
System.out.println("\n=== Java序列化备忘录模式演示完成 ===");
}
}
2. Spring State Machine - 状态机
// Spring状态机中的备忘录模式应用
// Spring State Machine Memento Pattern Application
// 订单状态枚举 - Order State Enum
public enum OrderState {
CREATED, PAID, SHIPPED, DELIVERED, CANCELLED
}
// 订单事件枚举 - Order Event Enum
public enum OrderEvent {
PAY, SHIP, DELIVER, CANCEL
}
// 订单状态机配置 - Order State Machine Configuration
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderState, OrderEvent> {
@Override
public void configure(StateMachineStateConfigurer<OrderState, OrderEvent> states) throws Exception {
states
.withStates()
.initial(OrderState.CREATED)
.state(OrderState.CREATED)
.state(OrderState.PAID)
.state(OrderState.SHIPPED)
.state(OrderState.DELIVERED)
.end(OrderState.CANCELLED);
}
@Override
public void configure(StateMachineTransitionConfigurer<OrderState, OrderEvent> transitions) throws Exception {
transitions
.withExternal().source(OrderState.CREATED).target(OrderState.PAID).event(OrderEvent.PAY)
.and()
.withExternal().source(OrderState.PAID).target(OrderState.SHIPPED).event(OrderEvent.SHIP)
.and()
.withExternal().source(OrderState.SHIPPED).target(OrderState.DELIVERED).event(OrderEvent.DELIVER)
.and()
.withExternal().source(OrderState.CREATED).target(OrderState.CANCELLED).event(OrderEvent.CANCEL)
.and()
.withExternal().source(OrderState.PAID).target(OrderState.CANCELLED).event(OrderEvent.CANCEL);
}
}
// 订单实体 - Order Entity
@Entity
@Table(name = "orders")
public class Order {
@Id
private String orderId;
private String customerName;
private BigDecimal amount;
@Enumerated(EnumType.STRING)
private OrderState state;
@Column(columnDefinition = "TEXT")
private String stateMachineContext; // 存储状态机上下文(备忘录)
// 构造函数、getters和setters
public Order() {}
public Order(String orderId, String customerName, BigDecimal amount) {
this.orderId = orderId;
this.customerName = customerName;
this.amount = amount;
this.state = OrderState.CREATED;
}
// Getters and setters
public String getOrderId() { return orderId; }
public void setOrderId(String orderId) { this.orderId = orderId; }
public String getCustomerName() { return customerName; }
public void setCustomerName(String customerName) { this.customerName = customerName; }
public BigDecimal getAmount() { return amount; }
public void setAmount(BigDecimal amount) { this.amount = amount; }
public OrderState getState() { return state; }
public void setState(OrderState state) { this.state = state; }
public String getStateMachineContext() { return stateMachineContext; }
public void setStateMachineContext(String stateMachineContext) { this.stateMachineContext = stateMachineContext; }
}
// 订单服务 - Order Service
@Service
public class OrderService {
private static final Logger logger = LoggerFactory.getLogger(OrderService.class);
private final StateMachine<OrderState, OrderEvent> stateMachine;
private final OrderRepository orderRepository;
@Autowired
public OrderService(StateMachine<OrderState, OrderEvent> stateMachine,
OrderRepository orderRepository) {
this.stateMachine = stateMachine;
this.orderRepository = orderRepository;
}
// 创建备忘录(保存状态机上下文)
public String createStateMachineMemento() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
stateMachine.getExtendedState().getVariables().forEach((key, value) -> {
try {
oos.writeObject(new AbstractMap.SimpleEntry<>(key, value));
} catch (IOException e) {
logger.error("序列化状态机变量失败", e);
}
});
oos.close();
return Base64.getEncoder().encodeToString(baos.toByteArray());
} catch (IOException e) {
logger.error("创建状态机备忘录失败", e);
throw new RuntimeException("创建备忘录失败", e);
}
}
// 从备忘录恢复(恢复状态机上下文)
public void restoreStateMachineFromMemento(String mementoData) {
try {
byte[] data = Base64.getDecoder().decode(mementoData);
ByteArrayInputStream bais = new ByteArrayInputStream(data);
ObjectInputStream ois = new ObjectInputStream(bais);
ExtendedState extendedState = stateMachine.getExtendedState();
while (bais.available() > 0) {
try {
@SuppressWarnings("unchecked")
Map.Entry<String, Object> entry = (Map.Entry<String, Object>) ois.readObject();
extendedState.getVariables().put(entry.getKey(), entry.getValue());
} catch (EOFException e) {
break;
}
}
ois.close();
System.out.println("从备忘录恢复状态机上下文");
} catch (IOException | ClassNotFoundException e) {
logger.error("恢复状态机备忘录失败", e);
throw new RuntimeException("恢复备忘录失败", e);
}
}
// 处理订单事件
@Transactional
public void processOrderEvent(String orderId, OrderEvent event) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new IllegalArgumentException("订单不存在: " + orderId));
// 恢复状态机状态
if (order.getStateMachineContext() != null) {
restoreStateMachineFromMemento(order.getStateMachineContext());
}
// 设置当前状态
stateMachine.stop();
stateMachine.getStateMachineAccessor().doWithAllRegions(access ->
access.resetStateMachine(new DefaultStateMachineContext<>(order.getState(), null, null, null)));
// 发送事件
Message<OrderEvent> message = MessageBuilder
.withPayload(event)
.setHeader("orderId", orderId)
.build();
stateMachine.start();
stateMachine.sendEvent(message);
// 获取新状态
OrderState newState = stateMachine.getState().getId();
order.setState(newState);
// 保存状态机上下文(创建备忘录)
String newContext = createStateMachineMemento();
order.setStateMachineContext(newContext);
orderRepository.save(order);
logger.info("订单状态更新: {} -> {} for order: {}", order.getState(), newState, orderId);
}
// 创建订单并初始化状态机
@Transactional
public Order createOrder(String orderId, String customerName, BigDecimal amount) {
Order order = new Order(orderId, customerName, amount);
// 初始化状态机
stateMachine.stop();
stateMachine.start();
// 创建初始备忘录
String initialContext = createStateMachineMemento();
order.setStateMachineContext(initialContext);
order = orderRepository.save(order);
logger.info("创建订单: {}", orderId);
return order;
}
// 获取订单当前状态
public OrderState getOrderState(String orderId) {
return orderRepository.findById(orderId)
.map(Order::getState)
.orElseThrow(() -> new IllegalArgumentException("订单不存在: " + orderId));
}
}
// 客户端使用 - Client Usage
@SpringBootApplication
public class SpringStateMachineMementoDemo {
public static void main(String[] args) {
SpringApplication.run(SpringStateMachineMementoDemo.class, args);
System.out.println("=== Spring状态机备忘录模式演示 ===");
System.out.println("启动Spring Boot应用,测试状态机备忘录功能");
}
@Component
public static class StateMachineMementoDemoRunner implements CommandLineRunner {
@Autowired
private OrderService orderService;
@Override
public void run(String... args) throws Exception {
System.out.println("\n--- 开始Spring状态机备忘录模式演示 ---");
// 创建订单
String orderId = "ORDER-" + System.currentTimeMillis();
Order order = orderService.createOrder(orderId, "张三", new BigDecimal("299.99"));
System.out.println("订单初始状态: " + order.getState());
// 处理订单事件
System.out.println("\n--- 处理订单事件 ---");
orderService.processOrderEvent(orderId, OrderEvent.PAY);
System.out.println("支付后状态: " + orderService.getOrderState(orderId));
orderService.processOrderEvent(orderId, OrderEvent.SHIP);
System.out.println("发货后状态: " + orderService.getOrderState(orderId));
orderService.processOrderEvent(orderId, OrderEvent.DELIVER);
System.out.println("送达后状态: " + orderService.getOrderState(orderId));
System.out.println("\nSpring状态机备忘录模式演示完成");
}
}
}
备忘录模式与其他模式比较 (Comparison with Other Patterns)
| 特性 | 备忘录模式 | 命令模式 | 原型模式 | 状态模式 |
|---|---|---|---|---|
| 目的 | 状态保存恢复 | 请求封装 | 对象复制 | 状态管理 |
| 关注点 | 状态快照 | 操作记录 | 对象克隆 | 状态转换 |
| 使用场景 | 撤销/重做 | 撤销/重做 | 对象复制 | 状态机 |
| 封装性 | 保持封装 | 可暴露操作 | 暴露克隆 | 状态封装 |
| 实现复杂度 | 中等 | 高 | 低 | 中等 |
最佳实践 (Best Practices)
1. 备忘录设计 (Memento Design)
- 保持备忘录的不可变性
- 只保存必要的状态信息
- 考虑使用窄接口保护原发器的封装性
2. 原发器设计 (Originator Design)
- 明确定义需要保存的状态
- 提供清晰的创建和恢复方法
- 考虑状态的验证和一致性检查
3. 管理者设计 (Caretaker Design)
- 合理管理备忘录的生命周期
- 考虑使用栈结构支持撤销/重做
- 实现适当的内存管理策略
4. 性能优化 (Performance Optimization)
- 使用增量备忘录减少内存占用
- 考虑使用对象池重用备忘录对象
- 实现适当的清理机制
5. 错误处理 (Error Handling)
- 处理备忘录创建和恢复过程中的异常
- 提供状态验证机制
- 考虑实现回滚机制
优缺点 (Pros and Cons)
优点 (Advantages)
- 保持封装性:不破坏对象的封装边界
- 简化原发器:原发器不需要管理多个版本的状态
- 支持撤销/重做:可以轻松实现撤销和重做功能
- 状态历史:可以维护对象的状态历史
- 检查点机制:支持创建状态检查点
缺点 (Disadvantages)
- 内存消耗:保存大量状态可能消耗大量内存
- 性能开销:创建和恢复备忘录需要时间
- 实现复杂:需要仔细设计备忘录接口
- 循环引用:可能导致循环引用问题
- 状态一致性:需要确保状态的一致性
实际应用建议 (Practical Application Tips)
1. 状态选择
- 只保存必要的状态信息
- 避免保存冗余或计算得出的状态
- 考虑状态的粒度和精度
2. 内存管理
- 实现适当的清理机制
- 考虑使用压缩技术减少内存占用
- 合理设置备忘录数量限制
3. 并发处理
- 在并发环境下确保线程安全
- 考虑使用不可变对象
- 处理好状态的同步问题
4. 测试策略
- 测试备忘录的创建和恢复
- 验证状态的一致性
- 测试边界情况和异常处理
总结 (Summary)
备忘录模式是一种优雅的行为型设计模式,它通过在不破坏封装性的前提下捕获和保存对象的内部状态,为撤销/重做、状态回滚和历史记录等功能提供了强大的支持。这种模式在现代软件系统中有着广泛的应用,从文本编辑器的撤销功能到游戏存档系统,再到复杂的状态管理。
在实际应用中,备忘录模式特别适合以下场景:
- 需要实现撤销/重做功能的应用程序
- 需要保存对象历史状态的系统
- 需要支持状态回滚的事务处理
- 需要创建状态检查点的长时间运行程序
- 需要实现状态快照的调试工具
通过合理使用备忘录模式,可以构建出功能丰富、用户友好的系统。然而,需要注意内存管理和性能优化,避免过度使用导致系统资源消耗过大。在设计时应该权衡好功能需求和系统资源之间的关系,确保系统的可扩展性和可维护性。
更多推荐



所有评论(0)