本文目录导读:

Java备忘录模式详解
备忘录模式(Memento Pattern)用于在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便将来恢复。
核心角色
- 发起人(Originator):需要保存状态的对象
- 备忘录(Memento):存储发起人内部状态
- 管理者(Caretaker):负责保存和管理备忘录
经典案例:游戏存档系统
// 1. 备忘录类 - 存储游戏状态
class GameMemento {
private int level;
private int health;
private String position;
private int score;
public GameMemento(int level, int health, String position, int score) {
this.level = level;
this.health = health;
this.position = position;
this.score = score;
}
// 只提供getter方法,不提供setter(保持不可变性)
public int getLevel() { return level; }
public int getHealth() { return health; }
public String getPosition() { return position; }
public int getScore() { return score; }
}
// 2. 发起人类 - 游戏角色
class GameCharacter implements Cloneable {
private int level;
private int health;
private String position;
private int score;
public GameCharacter() {
this.level = 1;
this.health = 100;
this.position = "起点";
this.score = 0;
}
// 创建备忘录(保存状态)
public GameMemento save() {
return new GameMemento(level, health, position, score);
}
// 从备忘录恢复状态
public void restore(GameMemento memento) {
if (memento == null) {
throw new IllegalArgumentException("备忘录不能为空");
}
this.level = memento.getLevel();
this.health = memento.getHealth();
this.position = memento.getPosition();
this.score = memento.getScore();
}
// 游戏操作
public void fight(int damage) {
this.health -= damage;
System.out.println("战斗受伤,当前生命值: " + health);
}
public void moveTo(String newPosition, int scoreGain) {
this.position = newPosition;
this.score += scoreGain;
System.out.println("移动到: " + position + ", 得分: " + score);
}
public void levelUp() {
this.level++;
this.health = 100;
System.out.println("升级到等级: " + level + ", 生命恢复满");
}
@Override
public String toString() {
return String.format("等级:%d, 生命:%d, 位置:%s, 分数:%d",
level, health, position, score);
}
}
// 3. 管理者类 - 存档管理器
class GameSaveManager {
private Stack<GameMemento> saveSlots = new Stack<>();
private int maxSlots;
public GameSaveManager(int maxSlots) {
this.maxSlots = maxSlots;
}
// 存档(压入栈中)
public void saveGame(GameCharacter character) {
if (saveSlots.size() >= maxSlots) {
System.out.println("存档已满,覆盖最早的存档");
saveSlots.remove(0);
}
saveSlots.push(character.save());
System.out.println("游戏已保存,当前存档数: " + saveSlots.size());
}
// 读档(从栈顶取出)
public void loadGame(GameCharacter character) {
if (saveSlots.isEmpty()) {
System.out.println("没有可用的存档");
return;
}
GameMemento memento = saveSlots.pop();
character.restore(memento);
System.out.println("游戏已加载");
}
// 查看所有存档
public void listSaves() {
System.out.println("当前存档数量: " + saveSlots.size());
// 注意:这里无法查看备忘录的具体内容,因为备忘录是私有的
}
}
// 4. 客户端代码
public class MementoPatternDemo {
public static void main(String[] args) {
GameCharacter player = new GameCharacter();
GameSaveManager saveManager = new GameSaveManager(3);
System.out.println("初始状态:");
System.out.println(player);
// 第一次存档
saveManager.saveGame(player);
// 游戏进行
player.moveTo("森林", 50);
player.fight(30);
System.out.println("战斗后: " + player);
// 第二次存档
saveManager.saveGame(player);
// 继续游戏
player.levelUp();
player.moveTo("城堡", 100);
player.fight(40);
System.out.println("继续游戏后: " + player);
// 读档(回到第二次存档)
System.out.println("\n读档操作:");
saveManager.loadGame(player);
System.out.println("读档后: " + player);
// 再读档(回到第一次存档)
System.out.println("\n再次读档:");
saveManager.loadGame(player);
System.out.println("读档后: " + player);
}
}
输出示例
初始状态:
等级:1, 生命:100, 位置:起点, 分数:0
游戏已保存,当前存档数: 1
移动到: 森林, 得分: 50
战斗受伤,当前生命值: 70
战斗后: 等级:1, 生命:70, 位置:森林, 分数:50
游戏已保存,当前存档数: 2
升级到等级: 2, 生命恢复满
移动到: 城堡, 得分: 150
战斗受伤,当前生命值: 60
继续游戏后: 等级:2, 生命:60, 位置:城堡, 分数:150
读档操作:
游戏已加载
读档后: 等级:1, 生命:70, 位置:森林, 分数:50
再次读档:
游戏已加载
读档后: 等级:1, 生命:100, 位置:起点, 分数:0
实际应用场景
文本编辑器撤销功能
class TextEditor {
private StringBuilder content;
private Stack<EditorMemento> history;
public void write(String text) {
history.push(save());
content.append(text);
}
public void undo() {
if (!history.isEmpty()) {
restore(history.pop());
}
}
}
数据库事务回滚
class DatabaseTransaction {
private Map<String, Object> state;
private List<TransactionMemento> savepoints;
public void begin() {
savepoints.clear();
savepoints.add(save()); // 保存初始状态
}
public void rollback() {
if (!savepoints.isEmpty()) {
restore(savepoints.get(0));
}
}
}
注意事项
优点
- 状态还原:可以轻松实现撤销/重做功能
- 封装性:备忘录对象不暴露内部状态给外部
- 简化发起人:发起人不需管理历史状态
缺点
- 资源消耗:如果状态很大,频繁保存会消耗内存
- 实现复杂度:需要额外的备忘录和管理者类
- 语言限制:Java中需要额外处理Object的克隆
最佳实践
- 使用浅拷贝/深拷贝:确保状态对象的完整复制
- 管理内存:限制存档数量或使用压缩存储
- 序列化支持:对于复杂的对象,考虑使用序列化
与其他模式结合
- 命令模式:用于实现更复杂的撤销/重做
- 原型模式:通过克隆简化状态保存
备忘录模式非常适合需要提供撤销功能的应用,如文本编辑器、游戏存档、绘图软件等场景。