一、核心概念
备忘录模式是一种行为型设计模式,它允许在不破坏封装性的前提下,捕获并外部化一个对象的内部状态,以便在以后可以将该对象恢复到原先保存的状态。
设计思想
- 状态封装:将对象状态保存到独立的对象中,不暴露实现细节
- 撤销/重做支持:提供历史状态管理机制
- 封装保护:确保只有原始对象能访问其状态的完整表示
- 快照机制:创建对象在某一时刻的状态快照
二、模式结构
类图示意
复制
┌─────────────────┐ ┌───────────────┐ ┌─────────────────┐
│ Originator │────────▶│ Memento │◀────────│ Caretaker │
│ (发起人) │ │ (备忘录) │ │ (负责人) │
├─────────────────┤ ├───────────────┤ ├─────────────────┤
│ +createMemento()│ │ -state │ │ -mementos │
│ +restore() │ │ +getState() │ │ +addMemento() │
│ +setState() │ │ +setState() │ │ +getMemento() │
│ +getState() │ └───────────────┘ └─────────────────┘
└─────────────────┘
核心角色
-
Originator (发起人)
- 需要保存状态的对象
- 创建备忘录以保存当前状态
- 使用备忘录恢复先前状态
-
Memento (备忘录)
- 存储发起人的内部状态
- 保护对状态的访问(通常有宽接口和窄接口)
-
Caretaker (负责人/管理者)
- 负责保存备忘录
- 不操作或检查备忘录内容
- 维护备忘录历史栈
三、Java代码实现
基础实现示例
java
java
下载
复制
// 1. 备忘录类 - 存储文本编辑器的状态
public class TextEditorMemento {
private final String text;
private final int cursorPosition;
private final String selection;
private final long timestamp;
// 包级访问权限,确保只有同包内的Originator能创建
TextEditorMemento(String text, int cursorPosition, String selection) {
this.text = text;
this.cursorPosition = cursorPosition;
this.selection = selection;
this.timestamp = System.currentTimeMillis();
}
// 包级访问权限的getter
String getText() { return text; }
int getCursorPosition() { return cursorPosition; }
String getSelection() { return selection; }
long getTimestamp() { return timestamp; }
// 公共方法 - 不暴露状态细节
public String getPreview() {
int previewLength = Math.min(text.length(), 50);
String preview = text.substring(0, previewLength);
if (text.length() > 50) preview += "...";
return preview;
}
}
// 2. 发起人类 - 文本编辑器
public class TextEditor {
private String text = "";
private int cursorPosition = 0;
private String selection = "";
// 业务方法
public void type(String input) {
if (!selection.isEmpty()) {
// 如果有选中文本,替换它
deleteSelection();
}
text = text.substring(0, cursorPosition) + input + text.substring(cursorPosition);
cursorPosition += input.length();
System.out.println("输入: " + input);
}
public void deleteSelection() {
if (!selection.isEmpty()) {
text = text.replace(selection, "");
selection = "";
System.out.println("删除选中文本");
}
}
public void select(int start, int end) {
if (start >= 0 && end <= text.length() && start < end) {
selection = text.substring(start, end);
cursorPosition = end;
System.out.println("选中文本: " + selection);
}
}
public void moveCursor(int position) {
if (position >= 0 && position <= text.length()) {
cursorPosition = position;
System.out.println("移动光标到: " + position);
}
}
// 备忘录相关方法
public TextEditorMemento createMemento() {
System.out.println("创建备忘录 - 当前文本长度: " + text.length());
return new TextEditorMemento(text, cursorPosition, selection);
}
public void restoreFromMemento(TextEditorMemento memento) {
this.text = memento.getText();
this.cursorPosition = memento.getCursorPosition();
this.selection = memento.getSelection();
System.out.println("从备忘录恢复 - 文本长度: " + text.length());
}
// 状态显示
public void display() {
System.out.println("=".repeat(50));
System.out.println("当前文本: " + text);
System.out.println("光标位置: " + cursorPosition);
System.out.println("选中文本: " + (selection.isEmpty() ? "[无]" : selection));
System.out.println("=".repeat(50));
}
// Getter/Setter
public String getText() { return text; }
public int getCursorPosition() { return cursorPosition; }
public String getSelection() { return selection; }
}
// 3. 负责人类 - 支持撤销/重做
public class EditorHistory {
private Stack<TextEditorMemento> undoStack = new Stack<>();
private Stack<TextEditorMemento> redoStack = new Stack<>();
private final int maxHistorySize;
public EditorHistory() {
this(50); // 默认保存50个历史状态
}
public EditorHistory(int maxHistorySize) {
this.maxHistorySize = maxHistorySize;
}
public void saveState(TextEditor editor) {
// 保存前检查是否与最近状态相同(避免保存重复状态)
if (!undoStack.isEmpty()) {
TextEditorMemento last = undoStack.peek();
if (statesAreEqual(last, editor.createMemento())) {
return; // 状态未变化,不保存
}
}
TextEditorMemento memento = editor.createMemento();
undoStack.push(memento);
redoStack.clear(); // 新操作清除重做栈
// 限制历史记录大小
if (undoStack.size() > maxHistorySize) {
removeOldestState();
}
System.out.println("状态已保存,历史记录数: " + undoStack.size());
}
public void undo(TextEditor editor) {
if (canUndo()) {
// 保存当前状态到重做栈
redoStack.push(editor.createMemento());
// 恢复到上一个状态
TextEditorMemento memento = undoStack.pop();
editor.restoreFromMemento(memento);
System.out.println("已撤销,剩余可撤销次数: " + undoStack.size());
} else {
System.out.println("无法撤销 - 已到历史记录开头");
}
}
public void redo(TextEditor editor) {
if (canRedo()) {
// 保存当前状态到撤销栈
undoStack.push(editor.createMemento());
// 恢复到重做状态
TextEditorMemento memento = redoStack.pop();
editor.restoreFromMemento(memento);
System.out.println("已重做,剩余可重做次数: " + redoStack.size());
} else {
System.out.println("无法重做 - 已到历史记录末尾");
}
}
public boolean canUndo() {
return undoStack.size() > 1; // 至少需要2个状态才能撤销
}
public boolean canRedo() {
return !redoStack.isEmpty();
}
public void clearHistory() {
undoStack.clear();
redoStack.clear();
System.out.println("历史记录已清除");
}
public void showHistory() {
System.out.println("\n=== 历史记录 ===");
System.out.println("撤销栈 (" + undoStack.size() + " 个状态):");
for (int i = 0; i < undoStack.size(); i++) {
TextEditorMemento memento = undoStack.get(i);
System.out.printf(" %d. [%tT] %s%n",
i + 1,
new Date(memento.getTimestamp()),
memento.getPreview());
}
if (!redoStack.isEmpty()) {
System.out.println("重做栈 (" + redoStack.size() + " 个状态):");
for (int i = 0; i < redoStack.size(); i++) {
TextEditorMemento memento = redoStack.get(i);
System.out.printf(" R%d. [%tT] %s%n",
i + 1,
new Date(memento.getTimestamp()),
memento.getPreview());
}
}
}
// 私有辅助方法
private boolean statesAreEqual(TextEditorMemento m1, TextEditorMemento m2) {
return m1.getText().equals(m2.getText()) &&
m1.getCursorPosition() == m2.getCursorPosition() &&
m1.getSelection().equals(m2.getSelection());
}
private void removeOldestState() {
if (!undoStack.isEmpty()) {
// 移除最旧的备忘录(栈底)
List<TextEditorMemento> list = new ArrayList<>(undoStack);
list.remove(0);
undoStack = new Stack<>();
undoStack.addAll(list);
}
}
}
// 4. 客户端使用
public class MementoPatternDemo {
public static void main(String[] args) {
// 创建编辑器
TextEditor editor = new TextEditor();
EditorHistory history = new EditorHistory(10); // 最多保存10个状态
// 初始状态
System.out.println("=== 初始状态 ===");
editor.type("Hello");
history.saveState(editor);
editor.display();
// 继续编辑
System.out.println("\n=== 继续编辑 ===");
editor.type(" World!");
history.saveState(editor);
editor.display();
// 更多编辑操作
System.out.println("\n=== 更多操作 ===");
editor.type("\nThis is a test.");
history.saveState(editor);
editor.select(6, 11); // 选中"World"
history.saveState(editor);
editor.type("Everyone"); // 替换选中的文本
history.saveState(editor);
editor.display();
// 显示历史
history.showHistory();
// 执行撤销
System.out.println("\n=== 执行撤销 ===");
history.undo(editor);
editor.display();
history.undo(editor);
editor.display();
// 执行重做
System.out.println("\n=== 执行重做 ===");
history.redo(editor);
editor.display();
// 最终状态
System.out.println("\n=== 最终状态 ===");
editor.display();
history.showHistory();
}
}
高级实现:增量备忘录
java
java
下载
复制
// 1. 增量备忘录接口
public interface IncrementalMemento {
Object getState();
void applyTo(Originator originator);
IncrementalMemento getInverse();
}
// 2. 文本增量备忘录
public class TextDeltaMemento implements IncrementalMemento {
private final String addedText;
private final int position;
private final boolean isAddition; // true=添加, false=删除
public TextDeltaMemento(String text, int position, boolean isAddition) {
this.addedText = text;
this.position = position;
this.isAddition = isAddition;
}
@Override
public Object getState() {
return Map.of(
"text", addedText,
"position", position,
"isAddition", isAddition
);
}
@Override
public void applyTo(Originator originator) {
if (originator instanceof TextEditor) {
TextEditor editor = (TextEditor) originator;
editor.moveCursor(position);
if (isAddition) {
editor.type(addedText);
} else {
// 实现删除逻辑
// editor.delete(addedText.length());
}
}
}
@Override
public IncrementalMemento getInverse() {
return new TextDeltaMemento(addedText, position, !isAddition);
}
public String getAddedText() { return addedText; }
public int getPosition() { return position; }
public boolean isAddition() { return isAddition; }
}
// 3. 增量历史管理器
public class DeltaHistoryManager {
private List<IncrementalMemento> deltas = new ArrayList<>();
private int currentIndex = -1;
public void addDelta(IncrementalMemento delta) {
// 移除当前索引之后的所有deltas(如果存在)
if (currentIndex < deltas.size() - 1) {
deltas = deltas.subList(0, currentIndex + 1);
}
deltas.add(delta);
currentIndex++;
System.out.println("添加增量变更,总数: " + deltas.size());
}
public void undo(Originator originator) {
if (canUndo()) {
IncrementalMemento delta = deltas.get(currentIndex);
delta.getInverse().applyTo(originator);
currentIndex--;
System.out.println("撤销,当前索引: " + currentIndex);
}
}
public void redo(Originator originator) {
if (canRedo()) {
currentIndex++;
IncrementalMemento delta = deltas.get(currentIndex);
delta.applyTo(originator);
System.out.println("重做,当前索引: " + currentIndex);
}
}
public boolean canUndo() {
return currentIndex >= 0;
}
public boolean canRedo() {
return currentIndex < deltas.size() - 1;
}
public void compressHistory() {
if (deltas.size() > 100) {
// 压缩策略:每10个增量合并为1个完整快照
// 实际实现会更复杂
System.out.println("历史记录压缩...");
}
}
}
四、应用场景
1. 文本编辑器与IDE
java
java
下载
复制
// IDE编辑器状态管理
public class IDEEditorState implements Memento {
private final String filePath;
private final String content;
private final int cursorLine;
private final int cursorColumn;
private final List<String> undoStack;
private final Set<String> openFiles;
public IDEEditorState(String filePath, String content,
int cursorLine, int cursorColumn,
List<String> undoStack, Set<String> openFiles) {
this.filePath = filePath;
this.content = content;
this.cursorLine = cursorLine;
this.cursorColumn = cursorColumn;
this.undoStack = new ArrayList<>(undoStack);
this.openFiles = new HashSet<>(openFiles);
}
// 获取状态快照
public String getSnapshotInfo() {
return String.format("文件: %s, 行: %d, 列: %d, 内容长度: %d",
filePath, cursorLine, cursorColumn, content.length());
}
}
2. 游戏存档系统
java
java
下载
复制
// 游戏状态备忘录
public class GameSave implements Memento {
private final String playerName;
private final int level;
private final int health;
private final int mana;
private final Position position;
private final List<Item> inventory;
private final Map<String, Integer> questProgress;
private final Date saveTime;
public GameSave(String playerName, int level, int health, int mana,
Position position, List<Item> inventory,
Map<String, Integer> questProgress) {
this.playerName = playerName;
this.level = level;
this.health = health;
this.mana = mana;
this.position = position.clone();
this.inventory = new ArrayList<>(inventory);
this.questProgress = new HashMap<>(questProgress);
this.saveTime = new Date();
}
public String getSaveInfo() {
return String.format("%s - 等级%d - 生命值%d - 位置(%d,%d)",
playerName, level, health, position.x, position.y);
}
}
// 存档管理器
public class SaveGameManager {
private Map<String, GameSave> saveSlots = new HashMap<>();
private final int maxSlots = 10;
public void saveGame(String slotName, GameState gameState) {
if (saveSlots.size() >= maxSlots) {
// 自动删除最旧的存档
removeOldestSave();
}
GameSave save = gameState.createSave();
saveSlots.put(slotName, save);
// 持久化到文件
persistToFile(slotName, save);
System.out.println("游戏已保存: " + slotName);
}
public void loadGame(String slotName, GameState gameState) {
GameSave save = saveSlots.get(slotName);
if (save != null) {
gameState.restoreFromSave(save);
System.out.println("游戏已加载: " + slotName);
}
}
public void autoSave(GameState gameState) {
String slotName = "autosave_" + System.currentTimeMillis();
saveGame(slotName, gameState);
}
private void persistToFile(String slotName, GameSave save) {
// 实现文件持久化逻辑
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("saves/" + slotName + ".sav"))) {
oos.writeObject(save);
} catch (IOException e) {
System.err.println("保存失败: " + e.getMessage());
}
}
private void removeOldestSave() {
// 找到并删除最旧的存档
// 简化实现
if (!saveSlots.isEmpty()) {
String oldest = saveSlots.keySet().iterator().next();
saveSlots.remove(oldest);
}
}
}
3. 数据库事务回滚
java
java
下载
复制
// 数据库事务备忘录
public class TransactionMemento implements Memento {
private final String transactionId;
private final List<SQLCommand> executedCommands;
private final Map<String, Object> checkpointData;
public TransactionMemento(String transactionId,
List<SQLCommand> executedCommands,
Map<String, Object> checkpointData) {
this.transactionId = transactionId;
this.executedCommands = new ArrayList<>(executedCommands);
this.checkpointData = new HashMap<>(checkpointData);
}
public void rollback(Connection connection) throws SQLException {
// 执行逆操作
for (int i = executedCommands.size() - 1; i >= 0; i--) {
SQLCommand command = executedCommands.get(i);
command.undo(connection);
}
}
public void commit(Connection connection) throws SQLException {
// 确认提交,清除备忘录
executedCommands.clear();
checkpointData.clear();
}
}
// 事务管理器
public class TransactionManager {
private Stack<TransactionMemento> transactionStack = new Stack<>();
private Connection connection;
public void beginTransaction() {
String transactionId = generateTransactionId();
TransactionMemento memento = new TransactionMemento(
transactionId, new ArrayList<>(), new HashMap<>());
transactionStack.push(memento);
// 设置保存点
setSavepoint(transactionId);
}
public void commit() throws SQLException {
if (!transactionStack.isEmpty()) {
TransactionMemento memento = transactionStack.pop();
memento.commit(connection);
releaseSavepoint(memento.getTransactionId());
}
}
public void rollback() throws SQLException {
if (!transactionStack.isEmpty()) {
TransactionMemento memento = transactionStack.pop();
memento.rollback(connection);
rollbackToSavepoint(memento.getTransactionId());
}
}
public void rollbackTo(String transactionId) throws SQLException {
while (!transactionStack.isEmpty()) {
TransactionMemento memento = transactionStack.peek();
if (memento.getTransactionId().equals(transactionId)) {
break;
}
memento = transactionStack.pop();
memento.rollback(connection);
}
}
}
4. 配置管理
java
java
下载
复制
// 系统配置备忘录
public class SystemConfigMemento implements Memento {
private final Map<String, Object> config;
private final String version;
private final Date timestamp;
public SystemConfigMemento(Map<String, Object> config, String version) {
this.config = new HashMap<>(config);
this.version = version;
this.timestamp = new Date();
}
public Map<String, Object> getConfigSnapshot() {
return new HashMap<>(config);
}
public void restoreConfig(SystemConfig config) {
config.applySnapshot(this.config);
}
public String getConfigInfo() {
return String.format("配置版本: %s, 时间: %tF %tT, 条目数: %d",
version, timestamp, timestamp, config.size());
}
}
// 配置版本管理器
public class ConfigVersionManager {
private TreeMap<Date, SystemConfigMemento> versionHistory = new TreeMap<>();
private final int maxVersions = 50;
public void saveVersion(SystemConfig config, String versionName) {
SystemConfigMemento memento = config.createMemento(versionName);
versionHistory.put(memento.getTimestamp(), memento);
// 限制版本数量
if (versionHistory.size() > maxVersions) {
Date oldest = versionHistory.firstKey();
versionHistory.remove(oldest);
}
// 持久化到存储
persistVersion(memento);
}
public void restoreVersion(Date timestamp, SystemConfig config) {
SystemConfigMemento memento = versionHistory.get(timestamp);
if (memento != null) {
memento.restoreConfig(config);
System.out.println("已恢复配置版本: " + memento.getConfigInfo());
}
}
public void rollbackToPrevious(SystemConfig config) {
if (versionHistory.size() >= 2) {
Date current = versionHistory.lastKey();
Date previous = versionHistory.lowerKey(current);
restoreVersion(previous, config);
}
}
public List<String> getVersionHistory() {
return versionHistory.values().stream()
.map(SystemConfigMemento::getConfigInfo)
.collect(Collectors.toList());
}
}
五、优缺点分析
优点
- 封装性保护:不暴露对象内部状态细节
- 简化发起人:发起人不需要管理状态历史
- 支持撤销/重做:易于实现状态恢复功能
- 状态快照:可以创建任意时刻的状态快照
- 职责分离:状态保存与业务逻辑分离
- 扩展性:支持增量保存、选择性保存等变体
缺点
- 内存消耗:保存大量状态可能占用大量内存
- 性能开销:频繁创建备忘录可能影响性能
- 深拷贝问题:复杂对象图需要正确处理深拷贝
- 状态一致性:需要确保备忘录与原始对象状态一致
- 生命周期管理:备忘录可能需要手动清理
- 序列化限制:某些对象可能无法序列化
六、改进与变体
1. 增量备忘录模式
java
java
下载
复制
// 只保存变化的部分而不是完整状态
public class IncrementalMementoPattern {
public interface DiffCalculator<T> {
T calculateDiff(T oldState, T newState);
T applyDiff(T state, T diff);
T invertDiff(T diff);
}
public static class IncrementalHistory<T> {
private T baseState;
private List<T> diffs = new ArrayList<>();
private DiffCalculator<T> calculator;
public void saveState(T newState) {
if (diffs.isEmpty()) {
baseState = deepCopy(newState);
diffs.add(calculator.calculateDiff(null, newState));
} else {
T current = getCurrentState();
T diff = calculator.calculateDiff(current, newState);
if (!isZeroDiff(diff)) {
diffs.add(diff);
}
}
}
public T getStateAt(int index) {
T state = deepCopy(baseState);
for (int i = 0; i <= Math.min(index, diffs.size() - 1); i++) {
state = calculator.applyDiff(state, diffs.get(i));
}
return state;
}
public void compress() {
if (diffs.size() > 10) {
// 压缩策略:合并最近的差异
T newBase = getStateAt(diffs.size() - 5);
baseState = newBase;
diffs = diffs.subList(diffs.size() - 5, diffs.size());
}
}
}
}
2. 懒加载备忘录
java
java
下载
复制
// 延迟加载备忘录内容
public class LazyMemento<T> implements Memento {
private final String stateId;
private Supplier<T> stateLoader;
private T cachedState;
private boolean loaded = false;
public LazyMemento(String stateId, Supplier<T> stateLoader) {
this.stateId = stateId;
this.stateLoader = stateLoader;
}
public synchronized T getState() {
if (!loaded) {
cachedState = stateLoader.get();
loaded = true;
stateLoader = null; // 释放加载器
}
return cachedState;
}
public void unload() {
if (loaded) {
cachedState = null;
loaded = false;
}
}
public boolean isLoaded() {
return loaded;
}
}
3. 压缩备忘录
java
java
下载
复制
// 使用压缩算法减少内存占用
public class CompressedMemento<T extends Serializable> implements Memento {
private byte[] compressedData;
public CompressedMemento(T state) {
this.compressedData = compress(state);
}
@SuppressWarnings("unchecked")
public T getState() {
return (T) decompress(compressedData);
}
private byte[] compress(T state) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream gzos = new GZIPOutputStream(baos);
ObjectOutputStream oos = new ObjectOutputStream(gzos)) {
oos.writeObject(state);
oos.flush();
gzos.finish();
return baos.toByteArray();
} catch (IOException e) {
throw new RuntimeException("压缩失败", e);
}
}
private Object decompress(byte[] data) {
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
GZIPInputStream gzis = new GZIPInputStream(bais);
ObjectInputStream ois = new ObjectInputStream(gzis)) {
return ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("解压失败", e);
}
}
public double getCompressionRatio() {
// 估算压缩率
return 0.0; // 实际实现需要计算
}
}
4. 选择性备忘录
java
java
下载
复制
// 只保存部分重要状态
public class SelectiveMemento implements Memento {
private final Map<String, Object> selectedState;
private final Predicate<String> fieldFilter;
public SelectiveMemento(Object originator, Predicate<String> fieldFilter) {
this.fieldFilter = fieldFilter;
this.selectedState = new HashMap<>();
// 使用反射获取选定字段
Field[] fields = originator.getClass().getDeclaredFields();
for (Field field : fields) {
if (fieldFilter.test(field.getName())) {
try {
field.setAccessible(true);
Object value = field.get(originator);
if (value instanceof Cloneable) {
// 深拷贝克隆对象
selectedState.put(field.getName(), deepCopy(value));
} else {
selectedState.put(field.getName(), value);
}
} catch (IllegalAccessException e) {
// 忽略无法访问的字段
}
}
}
}
public void restoreTo(Object originator) {
for (Map.Entry<String, Object> entry : selectedState.entrySet()) {
try {
Field field = originator.getClass().getDeclaredField(entry.getKey());
field.setAccessible(true);
field.set(originator, entry.getValue());
} catch (Exception e) {
// 忽略恢复失败的字段
}
}
}
private Object deepCopy(Object obj) {
// 实现深拷贝逻辑
return null; // 简化实现
}
}
七、注意事项
1. 内存管理最佳实践
java
java
下载
复制
// 智能内存管理
public class SmartMementoManager<T> {
private final int maxMemoryMB;
private final LinkedList<Memento<T>> history = new LinkedList<>();
private long currentMemoryUsage = 0;
public SmartMementoManager(int maxMemoryMB) {
this.maxMemoryMB = maxMemoryMB;
}
public void saveMemento(Memento<T> memento) {
long mementoSize = estimateSize(memento);
// 检查内存限制
while (currentMemoryUsage + mementoSize > maxMemoryMB * 1024L * 1024L
&& !history.isEmpty()) {
removeOldest();
}
history.addLast(memento);
currentMemoryUsage += mementoSize;
// 自动压缩
if (history.size() > 100) {
compressHistory();
}
}
private void removeOldest() {
if (!history.isEmpty()) {
Memento<T> oldest = history.removeFirst();
currentMemoryUsage -= estimateSize(oldest);
}
}
private void compressHistory() {
// 压缩策略:只保留关键快照
if (history.size() > 20) {
List<Memento<T>> compressed = new ArrayList<>();
// 保留第一个、最后一个和均匀间隔的快照
compressed.add(history.getFirst());
for (int i = 1; i < history.size() - 1; i += 5) {
compressed.add(history.get(i));
}
compressed.add(history.getLast());
history.clear();
history.addAll(compressed);
recalculateMemoryUsage();
}
}
private long estimateSize(Memento<T> memento) {
// 估算备忘录大小
return 1024; // 简化实现
}
}
2. 线程安全实现
java
java
下载
复制
public class ThreadSafeMementoManager<T> {
private final List<Memento<T>> history = Collections.synchronizedList(new ArrayList<>());
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private volatile int currentIndex = -1;
public void save(Memento<T> memento) {
lock.writeLock().lock();
try {
// 移除当前位置之后的所有备忘录
if (currentIndex < history.size() - 1) {
history.subList(currentIndex + 1, history.size()).clear();
}
history.add(memento);
currentIndex++;
} finally {
lock.writeLock().unlock();
}
}
public Memento<T> undo() {
lock.writeLock().lock();
try {
if (currentIndex > 0) {
currentIndex--;
return history.get(currentIndex);
}
return null;
} finally {
lock.writeLock().unlock();
}
}
public Memento<T> redo() {
lock.writeLock().lock();
try {
if (currentIndex < history.size() - 1) {
currentIndex++;
return history.get(currentIndex);
}
return null;
} finally {
lock.writeLock().unlock();
}
}
public Memento<T> getCurrent() {
lock.readLock().lock();
try {
if (currentIndex >= 0 && currentIndex < history.size()) {
return history.get(currentIndex);
}
return null;
} finally {
lock.readLock().unlock();
}
}
}
3. 序列化注意事项
java
java
下载
复制
// 可序列化的备忘录
public class SerializableMemento implements Memento, Serializable {
private static final long serialVersionUID = 1L;
private final Serializable state;
private transient Object nonSerializablePart;
public SerializableMemento(Serializable state, Object nonSerializablePart) {
this.state = state;
this.nonSerializablePart = nonSerializablePart;
}
// 自定义序列化
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
// 处理非序列化部分
if (nonSerializablePart instanceof Serializable) {
oos.writeObject(nonSerializablePart);
} else {
oos.writeObject(null);
}
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject();
Object obj = ois.readObject();
if (obj != null) {
nonSerializablePart = obj;
}
}
// 版本兼容性
@SuppressWarnings("unused")
private void readObjectNoData() throws ObjectStreamException {
// 处理无数据情况
this.state = null;
this.nonSerializablePart = null;
}
}
4. 性能优化技巧
java
java
下载
复制
// 备忘录池
public class MementoPool<T> {
private final Queue<Memento<T>> pool = new LinkedList<>();
private final Supplier<Memento<T>> creator;
private final int maxPoolSize;
public MementoPool(Supplier<Memento<T>> creator, int maxPoolSize) {
this.creator = creator;
this.maxPoolSize = maxPoolSize;
}
public Memento<T> acquire() {
Memento<T> memento = pool.poll();
if (memento == null) {
memento = creator.get();
}
return memento;
}
public void release(Memento<T> memento) {
if (pool.size() < maxPoolSize) {
// 重置备忘录状态
resetMemento(memento);
pool.offer(memento);
}
// 否则让GC回收
}
private void resetMemento(Memento<T> memento) {
// 重置备忘录内部状态以便重用
if (memento instanceof Resettable) {
((Resettable) memento).reset();
}
}
interface Resettable {
void reset();
}
}
八、与其他模式的关系
1. 与命令模式结合
java
java
下载
复制
// 支持撤销的命令
public abstract class UndoableCommand implements Command {
protected Memento memento;
protected Originator originator;
public UndoableCommand(Originator originator) {
this.originator = originator;
}
@Override
public void execute() {
// 保存状态
memento = originator.createMemento();
doExecute();
}
@Override
public void undo() {
if (memento != null) {
originator.restoreFromMemento(memento);
}
}
protected abstract void doExecute();
}
2. 与原型模式结合
java
java
下载
复制
// 使用原型创建备忘录
public class PrototypeMemento<T extends Cloneable> implements Memento {
private T state;
public PrototypeMemento(T originator) {
this.state = cloneState(originator);
}
@SuppressWarnings("unchecked")
private T cloneState(T originator) {
try {
// 假设对象实现了Cloneable
Method cloneMethod = originator.getClass().getMethod("clone");
return (T) cloneMethod.invoke(originator);
} catch (Exception e) {
throw new RuntimeException("克隆失败", e);
}
}
public T getState() {
return cloneState(state);
}
}
3. 与观察者模式结合
java
java
下载
复制
// 状态变化通知
public class ObservableOriginator extends Originator {
private List<StateChangeListener> listeners = new ArrayList<>();
public void addListener(StateChangeListener listener) {
listeners.add(listener);
}
@Override
public Memento createMemento() {
Memento memento = super.createMemento();
notifyListeners(StateChangeEvent.SAVED);
return memento;
}
@Override
public void restoreFromMemento(Memento memento) {
super.restoreFromMemento(memento);
notifyListeners(StateChangeEvent.RESTORED);
}
private void notifyListeners(StateChangeEvent event) {
for (StateChangeListener listener : listeners) {
listener.onStateChanged(event);
}
}
}
九、实际应用建议
使用场景判断
适合使用备忘录模式的场景:
- 需要撤销/重做功能的应用
- 需要保存对象历史状态的系统
- 需要实现快照功能的系统
- 需要事务回滚的数据库系统
- 游戏存档/读档系统
- 配置管理系统
不适合的场景:
- 对象状态特别大,内存有限
- 状态变化非常频繁
- 只需要保存少量简单状态
- 对性能要求极高的实时系统
最佳实践
- 控制备忘录数量:实现自动清理和压缩机制
- 增量保存:只保存变化的部分
- 懒加载:延迟加载备忘录内容
- 使用不可变对象:确保备忘录状态不可变
- 实现序列化:支持持久化存储
- 添加版本控制:处理状态结构变化
- 监控内存使用:防止内存泄漏
- 提供清理接口:允许手动释放资源
性能优化策略
java
java
下载
复制
// 性能监控装饰器
public class MonitoredMementoManager<T> extends MementoManager<T> {
private final PerformanceMonitor monitor = new PerformanceMonitor();
@Override
public void save(Memento<T> memento) {
long startTime = System.nanoTime();
long startMemory = Runtime.getRuntime().freeMemory();
super.save(memento);
long endTime = System.nanoTime();
long endMemory = Runtime.getRuntime().freeMemory();
monitor.recordSave(endTime - startTime, startMemory - endMemory);
if (monitor.isPerformanceDegraded()) {
suggestOptimizations();
}
}
private void suggestOptimizations() {
System.out.println("性能建议:");
System.out.println("1. 考虑使用增量备忘录");
System.out.println("2. 增加保存间隔");
System.out.println("3. 实现备忘录压缩");
System.out.println("4. 调整历史记录大小限制");
}
}
十、总结
备忘录模式是处理对象状态保存和恢复的强大工具,特别适合需要撤销/重做、事务回滚、快照功能的系统。正确使用时,它能够在不破坏封装性的前提下提供灵活的状态管理能力。
关键要点:
- 严格保护备忘录的内部状态
- 合理控制内存使用,避免内存泄漏
- 根据需求选择合适的实现变体
- 考虑线程安全和性能优化
- 提供适当的清理和管理机制
在复杂系统中,备忘录模式通常与其他模式(如命令模式、原型模式)结合使用,以提供更完整的状态管理解决方案。