备忘录设计模式全方位深度解析

2 阅读14分钟

一、核心概念

备忘录模式是一种行为型设计模式,它允许在不破坏封装性的前提下,捕获并外部化一个对象的内部状态,以便在以后可以将该对象恢复到原先保存的状态。

设计思想

  • 状态封装:将对象状态保存到独立的对象中,不暴露实现细节
  • 撤销/重做支持:提供历史状态管理机制
  • 封装保护:确保只有原始对象能访问其状态的完整表示
  • 快照机制:创建对象在某一时刻的状态快照

二、模式结构

类图示意

复制
┌─────────────────┐         ┌───────────────┐         ┌─────────────────┐
│    Originator   │────────▶│    Memento    │◀────────│    Caretaker    │
│  (发起人)       │         │  (备忘录)     │         │  (负责人)       │
├─────────────────┤         ├───────────────┤         ├─────────────────┤
│ +createMemento()│         │ -state        │         │ -mementos       │
│ +restore()      │         │ +getState()   │         │ +addMemento()   │
│ +setState()     │         │ +setState()   │         │ +getMemento()   │
│ +getState()     │         └───────────────┘         └─────────────────┘
└─────────────────┘

核心角色

  1. Originator (发起人)

    • 需要保存状态的对象
    • 创建备忘录以保存当前状态
    • 使用备忘录恢复先前状态
  2. Memento (备忘录)

    • 存储发起人的内部状态
    • 保护对状态的访问(通常有宽接口和窄接口)
  3. 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. 封装性保护:不暴露对象内部状态细节
  2. 简化发起人:发起人不需要管理状态历史
  3. 支持撤销/重做:易于实现状态恢复功能
  4. 状态快照:可以创建任意时刻的状态快照
  5. 职责分离:状态保存与业务逻辑分离
  6. 扩展性:支持增量保存、选择性保存等变体

缺点

  1. 内存消耗:保存大量状态可能占用大量内存
  2. 性能开销:频繁创建备忘录可能影响性能
  3. 深拷贝问题:复杂对象图需要正确处理深拷贝
  4. 状态一致性:需要确保备忘录与原始对象状态一致
  5. 生命周期管理:备忘录可能需要手动清理
  6. 序列化限制:某些对象可能无法序列化

六、改进与变体

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);
        }
    }
}

九、实际应用建议

使用场景判断

适合使用备忘录模式的场景:

  1. 需要撤销/重做功能的应用
  2. 需要保存对象历史状态的系统
  3. 需要实现快照功能的系统
  4. 需要事务回滚的数据库系统
  5. 游戏存档/读档系统
  6. 配置管理系统

不适合的场景:

  1. 对象状态特别大,内存有限
  2. 状态变化非常频繁
  3. 只需要保存少量简单状态
  4. 对性能要求极高的实时系统

最佳实践

  1. 控制备忘录数量:实现自动清理和压缩机制
  2. 增量保存:只保存变化的部分
  3. 懒加载:延迟加载备忘录内容
  4. 使用不可变对象:确保备忘录状态不可变
  5. 实现序列化:支持持久化存储
  6. 添加版本控制:处理状态结构变化
  7. 监控内存使用:防止内存泄漏
  8. 提供清理接口:允许手动释放资源

性能优化策略

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. 调整历史记录大小限制");
    }
}

十、总结

备忘录模式是处理对象状态保存和恢复的强大工具,特别适合需要撤销/重做、事务回滚、快照功能的系统。正确使用时,它能够在不破坏封装性的前提下提供灵活的状态管理能力。

关键要点:

  1. 严格保护备忘录的内部状态
  2. 合理控制内存使用,避免内存泄漏
  3. 根据需求选择合适的实现变体
  4. 考虑线程安全和性能优化
  5. 提供适当的清理和管理机制

在复杂系统中,备忘录模式通常与其他模式(如命令模式、原型模式)结合使用,以提供更完整的状态管理解决方案。