本文目录导读:

我来详细讲解Java中的备忘录模式(Memento Pattern),这是一种用于保存和恢复对象状态的行为型设计模式。
备忘录模式的定义
备忘录模式允许在不破坏封装性的前提下,捕获并外部化一个对象的内部状态,以便在将来某个时刻可以将该对象恢复到之前保存的状态。
核心角色
- Originator(发起人):需要保存状态的对象
- Memento(备忘录):存储发起人内部状态的对象
- Caretaker(管理者):负责保存和管理备忘录对象
实现方式
基本实现(内部类方式)
// 发起人 - 需要保存状态的对象
class Originator {
private String state;
public void setState(String state) {
System.out.println("设置状态为: " + state);
this.state = state;
}
public String getState() {
return state;
}
// 创建备忘录
public Memento saveStateToMemento() {
return new Memento(state);
}
// 从备忘录恢复状态
public void getStateFromMemento(Memento memento) {
state = memento.getState();
}
// 备忘录类 - 作为内部类实现,保证封装性
public static class Memento {
private final String state;
private Memento(String state) {
this.state = state;
}
private String getState() {
return state;
}
}
}
// 管理者 - 负责保存备忘录
class Caretaker {
private List<Originator.Memento> mementoList = new ArrayList<>();
public void add(Originator.Memento state) {
mementoList.add(state);
}
public Originator.Memento get(int index) {
return mementoList.get(index);
}
}
// 测试代码
public class MementoPatternDemo {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
// 保存状态
originator.setState("状态 #1");
caretaker.add(originator.saveStateToMemento());
originator.setState("状态 #2");
caretaker.add(originator.saveStateToMemento());
originator.setState("状态 #3");
// 恢复到状态 #1
originator.getStateFromMemento(caretaker.get(0));
System.out.println("恢复后的状态: " + originator.getState());
}
}
支持多状态保存的版本
import java.util.HashMap;
import java.util.Map;
// 更复杂的发起人
class ComplexOriginator {
private String name;
private int health;
private int mana;
private int level;
private Map<String, Integer> inventory;
public ComplexOriginator(String name) {
this.name = name;
this.health = 100;
this.mana = 50;
this.level = 1;
this.inventory = new HashMap<>();
}
public void attack(int damage) {
health -= damage;
}
public void addItem(String item, int quantity) {
inventory.put(item, inventory.getOrDefault(item, 0) + quantity);
}
public Memento save() {
return new Memento(name, health, mana, level, new HashMap<>(inventory));
}
public void restore(Memento memento) {
this.name = memento.getName();
this.health = memento.getHealth();
this.mana = memento.getMana();
this.level = memento.getLevel();
this.inventory = new HashMap<>(memento.getInventory());
}
@Override
public String toString() {
return String.format("角色: %s, 生命: %d, 魔法: %d, 等级: %d, 背包: %s",
name, health, mana, level, inventory);
}
// 备忘录类
public static class Memento {
private final String name;
private final int health;
private final int mana;
private final int level;
private final Map<String, Integer> inventory;
private Memento(String name, int health, int mana, int level,
Map<String, Integer> inventory) {
this.name = name;
this.health = health;
this.mana = mana;
this.level = level;
this.inventory = inventory;
}
// 只提供getter方法,不提供setter
public String getName() { return name; }
public int getHealth() { return health; }
public int getMana() { return mana; }
public int getLevel() { return level; }
public Map<String, Integer> getInventory() { return inventory; }
}
}
// 管理者
class GameCaretaker {
private Stack<ComplexOriginator.Memento> undoStack = new Stack<>();
private Stack<ComplexOriginator.Memento> redoStack = new Stack<>();
public void saveState(ComplexOriginator.Memento memento) {
undoStack.push(memento);
redoStack.clear(); // 新操作后清除重做栈
}
public ComplexOriginator.Memento undo() {
if (!undoStack.isEmpty()) {
ComplexOriginator.Memento memento = undoStack.pop();
redoStack.push(memento);
return undoStack.isEmpty() ? null : undoStack.peek();
}
return null;
}
public ComplexOriginator.Memento redo() {
if (!redoStack.isEmpty()) {
ComplexOriginator.Memento memento = redoStack.pop();
undoStack.push(memento);
return memento;
}
return null;
}
public boolean canUndo() {
return !undoStack.isEmpty();
}
public boolean canRedo() {
return !redoStack.isEmpty();
}
}
// 测试
public class GameSaveDemo {
public static void main(String[] args) {
ComplexOriginator player = new ComplexOriginator("勇士");
GameCaretaker caretaker = new GameCaretaker();
System.out.println("初始状态: " + player);
// 游戏进行中
player.attack(30);
player.addItem("生命药水", 2);
caretaker.saveState(player.save());
System.out.println("保存点1: " + player);
// 继续游戏
player.attack(50);
player.level = 3;
player.addItem("魔法水晶", 1);
caretaker.saveState(player.save());
System.out.println("保存点2: " + player);
// 撤销到保存点1
ComplexOriginator.Memento undoState = caretaker.undo();
if (undoState != null) {
player.restore(undoState);
System.out.println("撤销后: " + player);
}
// 重做
ComplexOriginator.Memento redoState = caretaker.redo();
if (redoState != null) {
player.restore(redoState);
System.out.println("重做后: " + player);
}
}
}
使用序列化的通用实现
import java.io.*;
// 使用Java序列化实现通用备忘录
class SerializableMemento<T extends Serializable> {
private byte[] stateData;
public void saveState(T originator) {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(originator);
stateData = bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
}
@SuppressWarnings("unchecked")
public T restoreState() {
try (ByteArrayInputStream bis = new ByteArrayInputStream(stateData);
ObjectInputStream ois = new ObjectInputStream(bis)) {
return (T) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
}
// 需要保存状态的对象实现Serializable
class GameCharacter implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int health;
private int mana;
private transient String tempData; // 不保存的临时数据
public GameCharacter(String name) {
this.name = name;
this.health = 100;
this.mana = 50;
this.tempData = "临时数据";
}
// getters and setters...
@Override
public String toString() {
return String.format("GameCharacter{name='%s', health=%d, mana=%d}",
name, health, mana);
}
}
实际应用场景
文本编辑器的撤销/重做功能
import java.util.Stack;
class TextEditor {
private StringBuilder content;
private Stack<String> history;
public TextEditor() {
this.content = new StringBuilder();
this.history = new Stack<>();
}
public void write(String text) {
history.push(content.toString()); // 保存当前状态
content.append(text);
}
public void undo() {
if (!history.isEmpty()) {
content = new StringBuilder(history.pop());
}
}
public String getContent() {
return content.toString();
}
}
数据库事务管理
class DatabaseTransaction {
private Map<String, Object> originalState;
private Map<String, Object> currentState;
public DatabaseTransaction(Map<String, Object> initialState) {
this.originalState = new HashMap<>(initialState);
this.currentState = new HashMap<>(initialState);
}
public Memento createMemento() {
return new Memento(new HashMap<>(currentState));
}
public void restoreMemento(Memento memento) {
this.currentState = new HashMap<>(memento.getState());
}
public static class Memento {
private final Map<String, Object> state;
private Memento(Map<String, Object> state) {
this.state = state;
}
private Map<String, Object> getState() {
return state;
}
}
}
优缺点分析
优点:
- 保持了封装边界:备忘录对象只对发起人可见
- 简化了发起人:不需要管理版本历史
- 支持撤销操作:方便实现撤销/重做功能
- 状态管理灵活:可以保存多个检查点
缺点:
- 资源消耗大:如果保存的状态很大,会占用大量内存
- 实现复杂度高:需要处理深拷贝和浅拷贝问题
- 可能带来性能问题:频繁保存和恢复状态会影响性能
最佳实践建议
- 使用深拷贝:确保备忘录中的状态是独立的副本
- 限制备忘录大小:设置最大保存数量或使用LRU算法
- 使用压缩:对于大量数据,考虑压缩存储
- 合理使用原型模式:通过克隆创建备忘录副本
- 考虑使用序列化:对于复杂对象,序列化可以简化实现
与其他模式的关系
- 命令模式:备忘录模式常用于实现命令模式的撤销功能
- 原型模式:备忘录可以使用原型模式创建对象副本
- 迭代器模式:可以用来遍历历史状态
备忘录模式是实现状态保存和恢复的强大工具,特别适合需要撤销/重做功能、保存检查点或实现事务回滚的场景。