Deepeek用大白话讲解 --> 备忘录模式(企业级场景1,撤销重做2,状态保存3,游戏存档4)

备忘录模式大白话讲解

一句话概括

就像游戏存档:你玩到一半可以保存进度,想玩了再读档,回到之前的状态


现实生活比喻

场景1:游戏存档

  • 游戏角色:当前状态(生命值、装备、位置)
  • 存档文件:备忘录(保存的状态)
  • 存档系统:管理者(保存和恢复存档)
  • 过程:玩游戏 → 存档 → 继续玩 → 读档回到存档点

场景2:文档编辑

  • 文档:当前内容
  • 撤销缓存:备忘录(保存的历史版本)
  • 编辑软件:管理者(保存和恢复历史)
  • 过程:编辑文档 → Ctrl+S保存 → 继续编辑 → 打开旧版本恢复

完整代码示例

场景1:游戏角色存档系统

java 复制代码
/**
 * 备忘录模式 - 游戏存档系统
 */
public class Main {
    public static void main(String[] args) {
        System.out.println("=== 游戏存档系统 ===");
        
        // 创建游戏角色(发起人)
        GameCharacter player = new GameCharacter("勇者", 1);
        player.setHealth(100);
        player.setMana(50);
        player.setWeapon("新手剑");
        player.setPosition("新手村");
        player.addItem("生命药水");
        player.addItem("魔法药水");
        
        System.out.println("初始状态:");
        player.displayStatus();
        
        // 创建存档管理器(管理者)
        SaveManager saveManager = new SaveManager();
        
        // 存档1:初始状态
        saveManager.save("初始存档", player.createSave());
        System.out.println("\n✓ 已保存:初始存档");
        
        // 玩游戏,状态变化
        System.out.println("\n--- 开始冒险 ---");
        player.setHealth(80);
        player.setMana(30);
        player.setWeapon("钢铁剑");
        player.setPosition("森林");
        player.addItem("熊皮");
        player.takeDamage(20);
        player.displayStatus();
        
        // 存档2:森林状态
        saveManager.save("森林存档", player.createSave());
        System.out.println("\n✓ 已保存:森林存档");
        
        // 继续冒险
        System.out.println("\n--- 继续冒险 ---");
        player.setHealth(40);
        player.setMana(10);
        player.setWeapon("火焰剑");
        player.setPosition("火山");
        player.addItem("龙鳞");
        player.addItem("红宝石");
        player.takeDamage(30);
        player.displayStatus();
        
        // 存档3:火山状态
        saveManager.save("火山存档", player.createSave());
        System.out.println("\n✓ 已保存:火山存档");
        
        // 显示所有存档
        System.out.println("\n=== 存档列表 ===");
        saveManager.showSaves();
        
        // 读档测试
        System.out.println("\n=== 读档测试 ===");
        
        // 读档1:回到森林存档
        System.out.println("\n--- 读档:森林存档 ---");
        player.restoreFromSave(saveManager.load("森林存档"));
        player.displayStatus();
        
        // 继续玩一会儿
        System.out.println("\n--- 继续游戏 ---");
        player.setHealth(60);
        player.setPosition("地下城");
        player.addItem("骷髅钥匙");
        player.displayStatus();
        
        // 读档2:回到火山存档
        System.out.println("\n--- 读档:火山存档 ---");
        player.restoreFromSave(saveManager.load("火山存档"));
        player.displayStatus();
        
        // 读档3:回到初始存档
        System.out.println("\n--- 读档:初始存档 ---");
        player.restoreFromSave(saveManager.load("初始存档"));
        player.displayStatus();
        
        // 测试:读取不存在的存档
        System.out.println("\n--- 测试:读取不存在的存档 ---");
        try {
            player.restoreFromSave(saveManager.load("不存在的存档"));
        } catch (IllegalArgumentException e) {
            System.out.println("错误:" + e.getMessage());
        }
    }
}

/**
 * 备忘录类 - 游戏存档
 */
class GameSave implements Cloneable {
    private String saveName;
    private String characterName;
    private int level;
    private int health;
    private int mana;
    private String weapon;
    private String position;
    private List<String> inventory;
    private Date saveTime;
    
    public GameSave(String characterName, int level, int health, int mana, 
                   String weapon, String position, List<String> inventory) {
        this.characterName = characterName;
        this.level = level;
        this.health = health;
        this.mana = mana;
        this.weapon = weapon;
        this.position = position;
        this.inventory = new ArrayList<>(inventory); // 深拷贝
        this.saveTime = new Date();
    }
    
    // 克隆方法,确保备忘录的不可变性
    @Override
    public GameSave clone() {
        try {
            GameSave cloned = (GameSave) super.clone();
            cloned.inventory = new ArrayList<>(this.inventory); // 深拷贝列表
            cloned.saveTime = (Date) this.saveTime.clone();
            return cloned;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
    
    public void setSaveName(String saveName) {
        this.saveName = saveName;
    }
    
    public String getSaveName() {
        return saveName;
    }
    
    public String getCharacterName() {
        return characterName;
    }
    
    public int getLevel() {
        return level;
    }
    
    public int getHealth() {
        return health;
    }
    
    public int getMana() {
        return mana;
    }
    
    public String getWeapon() {
        return weapon;
    }
    
    public String getPosition() {
        return position;
    }
    
    public List<String> getInventory() {
        return new ArrayList<>(inventory); // 返回副本,保护内部状态
    }
    
    public Date getSaveTime() {
        return (Date) saveTime.clone();
    }
    
    public void display() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("存档: " + saveName);
        System.out.println("  角色: " + characterName + " (Lv." + level + ")");
        System.out.println("  生命: " + health + " | 魔法: " + mana);
        System.out.println("  武器: " + weapon);
        System.out.println("  位置: " + position);
        System.out.println("  物品: " + String.join(", ", inventory));
        System.out.println("  时间: " + sdf.format(saveTime));
    }
}

/**
 * 发起人类 - 游戏角色
 */
class GameCharacter {
    private String name;
    private int level;
    private int health;
    private int maxHealth = 100;
    private int mana;
    private int maxMana = 100;
    private String weapon;
    private String position;
    private List<String> inventory;
    
    public GameCharacter(String name, int level) {
        this.name = name;
        this.level = level;
        this.health = maxHealth;
        this.mana = maxMana;
        this.weapon = "空手";
        this.position = "未知";
        this.inventory = new ArrayList<>();
    }
    
    // 创建存档(创建备忘录)
    public GameSave createSave() {
        return new GameSave(name, level, health, mana, weapon, position, inventory);
    }
    
    // 从存档恢复(恢复状态)
    public void restoreFromSave(GameSave save) {
        if (save == null) {
            throw new IllegalArgumentException("存档不存在");
        }
        
        this.name = save.getCharacterName();
        this.level = save.getLevel();
        this.health = save.getHealth();
        this.mana = save.getMana();
        this.weapon = save.getWeapon();
        this.position = save.getPosition();
        this.inventory = new ArrayList<>(save.getInventory());
        
        System.out.println("✓ 已读取存档: " + save.getSaveName());
    }
    
    // 角色操作方法
    public void takeDamage(int damage) {
        this.health = Math.max(0, this.health - damage);
        System.out.println("  受到 " + damage + " 点伤害,剩余生命: " + health);
    }
    
    public void heal(int amount) {
        this.health = Math.min(maxHealth, this.health + amount);
        System.out.println("  恢复 " + amount + " 点生命,剩余生命: " + health);
    }
    
    public void addItem(String item) {
        inventory.add(item);
        System.out.println("  获得物品: " + item);
    }
    
    public void removeItem(String item) {
        inventory.remove(item);
        System.out.println("  失去物品: " + item);
    }
    
    // Setter方法
    public void setHealth(int health) {
        this.health = Math.min(health, maxHealth);
    }
    
    public void setMana(int mana) {
        this.mana = Math.min(mana, maxMana);
    }
    
    public void setWeapon(String weapon) {
        this.weapon = weapon;
        System.out.println("  更换武器: " + weapon);
    }
    
    public void setPosition(String position) {
        this.position = position;
        System.out.println("  移动到: " + position);
    }
    
    // 显示状态
    public void displayStatus() {
        System.out.println("角色状态:");
        System.out.println("  名称: " + name + " (Lv." + level + ")");
        System.out.println("  生命: " + health + "/" + maxHealth);
        System.out.println("  魔法: " + mana + "/" + maxMana);
        System.out.println("  武器: " + weapon);
        System.out.println("  位置: " + position);
        System.out.println("  物品栏 (" + inventory.size() + "): " + 
                          (inventory.isEmpty() ? "空" : String.join(", ", inventory)));
    }
}

/**
 * 管理者类 - 存档管理器
 */
class SaveManager {
    private Map<String, GameSave> saves = new HashMap<>();
    
    // 保存存档
    public void save(String saveName, GameSave save) {
        GameSave clonedSave = save.clone(); // 保存副本
        clonedSave.setSaveName(saveName);
        saves.put(saveName, clonedSave);
    }
    
    // 读取存档
    public GameSave load(String saveName) {
        GameSave save = saves.get(saveName);
        if (save == null) {
            throw new IllegalArgumentException("存档不存在: " + saveName);
        }
        return save.clone(); // 返回副本,保护原始存档
    }
    
    // 删除存档
    public void delete(String saveName) {
        if (saves.remove(saveName) != null) {
            System.out.println("已删除存档: " + saveName);
        } else {
            System.out.println("存档不存在: " + saveName);
        }
    }
    
    // 显示所有存档
    public void showSaves() {
        if (saves.isEmpty()) {
            System.out.println("没有存档");
            return;
        }
        
        for (GameSave save : saves.values()) {
            save.display();
            System.out.println();
        }
    }
    
    // 获取存档数量
    public int getSaveCount() {
        return saves.size();
    }
}

运行结果

复制代码
=== 游戏存档系统 ===
初始状态:
角色状态:
  名称: 勇者 (Lv.1)
  生命: 100/100
  魔法: 50/100
  武器: 新手剑
  位置: 新手村
  物品栏 (2): 生命药水, 魔法药水

✓ 已保存:初始存档

--- 开始冒险 ---
  更换武器: 钢铁剑
  移动到: 森林
  获得物品: 熊皮
  受到 20 点伤害,剩余生命: 60
角色状态:
  名称: 勇者 (Lv.1)
  生命: 60/100
  魔法: 30/100
  武器: 钢铁剑
  位置: 森林
  物品栏 (3): 生命药水, 魔法药水, 熊皮

✓ 已保存:森林存档

--- 继续冒险 ---
  更换武器: 火焰剑
  移动到: 火山
  获得物品: 龙鳞
  获得物品: 红宝石
  受到 30 点伤害,剩余生命: 10
角色状态:
  名称: 勇者 (Lv.1)
  生命: 10/100
  魔法: 10/100
  武器: 火焰剑
  位置: 火山
  物品栏 (5): 生命药水, 魔法药水, 熊皮, 龙鳞, 红宝石

✓ 已保存:火山存档

=== 存档列表 ===
存档: 火山存档
  角色: 勇者 (Lv.1)
  生命: 10 | 魔法: 10
  武器: 火焰剑
  位置: 火山
  物品: 生命药水, 魔法药水, 熊皮, 龙鳞, 红宝石
  时间: 2024-01-19 14:30:45

存档: 森林存档
  角色: 勇者 (Lv.1)
  生命: 60 | 魔法: 30
  武器: 钢铁剑
  位置: 森林
  物品: 生命药水, 魔法药水, 熊皮
  时间: 2024-01-19 14:30:45

存档: 初始存档
  角色: 勇者 (Lv.1)
  生命: 100 | 魔法: 50
  武器: 新手剑
  位置: 新手村
  物品: 生命药水, 魔法药水
  时间: 2024-01-19 14:30:45

=== 读档测试 ===

--- 读档:森林存档 ---
✓ 已读取存档: 森林存档
角色状态:
  名称: 勇者 (Lv.1)
  生命: 60/100
  魔法: 30/100
  武器: 钢铁剑
  位置: 森林
  物品栏 (3): 生命药水, 魔法药水, 熊皮

--- 继续游戏 ---
  移动到: 地下城
  获得物品: 骷髅钥匙
角色状态:
  名称: 勇者 (Lv.1)
  生命: 60/100
  魔法: 30/100
  武器: 钢铁剑
  位置: 地下城
  物品栏 (4): 生命药水, 魔法药水, 熊皮, 骷髅钥匙

--- 读档:火山存档 ---
✓ 已读取存档: 火山存档
角色状态:
  名称: 勇者 (Lv.1)
  生命: 10/100
  魔法: 10/100
  武器: 火焰剑
  位置: 火山
  物品栏 (5): 生命药水, 魔法药水, 熊皮, 龙鳞, 红宝石

--- 读档:初始存档 ---
✓ 已读取存档: 初始存档
角色状态:
  名称: 勇者 (Lv.1)
  生命: 100/100
  魔法: 50/100
  武器: 新手剑
  位置: 新手村
  物品栏 (2): 生命药水, 魔法药水

--- 测试:读取不存在的存档 ---
错误:存档不存在: 不存在的存档



场景2:文本编辑器撤销/恢复功能

java 复制代码
/**
 * 备忘录模式 - 文本编辑器撤销/恢复
 */
public class TextEditorExample {
    public static void main(String[] args) {
        System.out.println("=== 文本编辑器(支持撤销/恢复) ===");
        
        // 创建文本编辑器
        TextEditor editor = new TextEditor();
        
        // 创建历史管理器
        EditorHistory history = new EditorHistory();
        
        // 编辑文档
        System.out.println("\n--- 开始编辑 ---");
        
        editor.type("Hello, ");
        editor.display();
        history.save(editor.save()); // 保存状态1
        
        editor.type("World!");
        editor.display();
        history.save(editor.save()); // 保存状态2
        
        editor.type(" This is a demo.");
        editor.display();
        history.save(editor.save()); // 保存状态3
        
        editor.delete(5); // 删除"demo."
        editor.display();
        history.save(editor.save()); // 保存状态4
        
        editor.type("memo pattern example.");
        editor.display();
        history.save(editor.save()); // 保存状态5
        
        // 显示历史
        System.out.println("\n=== 编辑历史 ===");
        history.showHistory();
        
        // 测试撤销/恢复
        System.out.println("\n=== 撤销操作 ===");
        
        System.out.println("\n撤销到上一步:");
        editor.restore(history.undo());
        editor.display();
        
        System.out.println("\n再撤销一步:");
        editor.restore(history.undo());
        editor.display();
        
        System.out.println("\n再撤销一步:");
        editor.restore(history.undo());
        editor.display();
        
        System.out.println("\n=== 恢复操作 ===");
        
        System.out.println("\n恢复一步:");
        editor.restore(history.redo());
        editor.display();
        
        System.out.println("\n再恢复一步:");
        editor.restore(history.redo());
        editor.display();
        
        System.out.println("\n再恢复一步:");
        editor.restore(history.redo());
        editor.display();
        
        // 测试连续撤销
        System.out.println("\n=== 测试连续撤销 ===");
        while (history.canUndo()) {
            System.out.println("\n撤销:");
            editor.restore(history.undo());
            editor.display();
        }
        
        // 测试连续恢复
        System.out.println("\n=== 测试连续恢复 ===");
        while (history.canRedo()) {
            System.out.println("\n恢复:");
            editor.restore(history.redo());
            editor.display();
        }
    }
}

/**
 * 备忘录类 - 编辑器状态
 */
class EditorState implements Cloneable {
    private String content;
    private Date timestamp;
    private int cursorPosition;
    
    public EditorState(String content, int cursorPosition) {
        this.content = content;
        this.cursorPosition = cursorPosition;
        this.timestamp = new Date();
    }
    
    @Override
    public EditorState clone() {
        try {
            EditorState cloned = (EditorState) super.clone();
            cloned.timestamp = (Date) this.timestamp.clone();
            return cloned;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
    
    public String getContent() {
        return content;
    }
    
    public int getCursorPosition() {
        return cursorPosition;
    }
    
    public Date getTimestamp() {
        return (Date) timestamp.clone();
    }
    
    public void display() {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        System.out.println("  内容: " + content);
        System.out.println("  光标位置: " + cursorPosition);
        System.out.println("  时间: " + sdf.format(timestamp));
    }
}

/**
 * 发起人类 - 文本编辑器
 */
class TextEditor {
    private String content;
    private int cursorPosition;
    
    public TextEditor() {
        this.content = "";
        this.cursorPosition = 0;
    }
    
    // 创建备忘录
    public EditorState save() {
        return new EditorState(content, cursorPosition);
    }
    
    // 从备忘录恢复
    public void restore(EditorState state) {
        if (state != null) {
            this.content = state.getContent();
            this.cursorPosition = state.getCursorPosition();
        }
    }
    
    // 编辑操作
    public void type(String text) {
        content = content.substring(0, cursorPosition) + 
                 text + 
                 content.substring(cursorPosition);
        cursorPosition += text.length();
        System.out.println("输入: \"" + text + "\"");
    }
    
    public void delete(int length) {
        if (cursorPosition >= length) {
            String deleted = content.substring(cursorPosition - length, cursorPosition);
            content = content.substring(0, cursorPosition - length) + 
                     content.substring(cursorPosition);
            cursorPosition -= length;
            System.out.println("删除: \"" + deleted + "\"");
        }
    }
    
    public void backspace(int length) {
        delete(length);
    }
    
    public void setCursor(int position) {
        if (position >= 0 && position <= content.length()) {
            cursorPosition = position;
            System.out.println("移动光标到位置: " + position);
        }
    }
    
    // 显示当前状态
    public void display() {
        System.out.println("当前文档:");
        System.out.println("  \"" + content + "\"");
        System.out.println("  光标位置: " + cursorPosition + 
                          " (字符: '" + 
                          (cursorPosition < content.length() && cursorPosition >= 0 ? 
                           content.charAt(cursorPosition) : ' ') + "')");
        System.out.println("  长度: " + content.length() + " 字符");
    }
}

/**
 * 管理者类 - 编辑器历史
 */
class EditorHistory {
    private Stack<EditorState> undoStack = new Stack<>();
    private Stack<EditorState> redoStack = new Stack<>();
    
    // 保存状态
    public void save(EditorState state) {
        undoStack.push(state.clone()); // 保存副本
        redoStack.clear(); // 新的保存清空重做栈
        System.out.println("✓ 已保存状态");
    }
    
    // 撤销
    public EditorState undo() {
        if (!canUndo()) {
            throw new IllegalStateException("无法撤销,没有更多历史记录");
        }
        
        // 弹出当前状态
        EditorState current = undoStack.pop();
        
        // 如果还有上一个状态,保存到重做栈
        if (!undoStack.isEmpty()) {
            redoStack.push(current);
            return undoStack.peek().clone(); // 返回上一个状态
        } else {
            // 回到空状态
            redoStack.push(current);
            return new EditorState("", 0);
        }
    }
    
    // 恢复
    public EditorState redo() {
        if (!canRedo()) {
            throw new IllegalStateException("无法恢复,没有更多重做记录");
        }
        
        EditorState state = redoStack.pop();
        undoStack.push(state.clone()); // 放回撤销栈
        return state.clone(); // 返回副本
    }
    
    public boolean canUndo() {
        return !undoStack.isEmpty();
    }
    
    public boolean canRedo() {
        return !redoStack.isEmpty();
    }
    
    // 显示历史
    public void showHistory() {
        System.out.println("撤销栈 (" + undoStack.size() + " 个状态):");
        for (int i = 0; i < undoStack.size(); i++) {
            System.out.print("  [" + i + "] ");
            undoStack.get(i).display();
        }
        
        System.out.println("\n重做栈 (" + redoStack.size() + " 个状态):");
        for (int i = redoStack.size() - 1; i >= 0; i--) {
            System.out.print("  [" + i + "] ");
            redoStack.get(i).display();
        }
    }
}

运行结果

复制代码
=== 文本编辑器(支持撤销/恢复) ===

--- 开始编辑 ---
输入: "Hello, "
✓ 已保存状态
当前文档:
  "Hello, "
  光标位置: 7 (字符: ' ')
  长度: 7 字符
输入: "World!"
✓ 已保存状态
当前文档:
  "Hello, World!"
  光标位置: 13 (字符: ' ')
  长度: 13 字符
输入: " This is a demo."
✓ 已保存状态
当前文档:
  "Hello, World! This is a demo."
  光标位置: 29 (字符: ' ')
  长度: 29 字符
删除: "demo."
✓ 已保存状态
当前文档:
  "Hello, World! This is a "
  光标位置: 24 (字符: ' ')
  长度: 24 字符
输入: "memo pattern example."
✓ 已保存状态
当前文档:
  "Hello, World! This is a memo pattern example."
  光标位置: 45 (字符: ' ')
  长度: 45 字符

=== 编辑历史 ===
撤销栈 (5 个状态):
  [0]   内容: Hello, 
        光标位置: 7
        时间: 14:35:22
  [1]   内容: Hello, World!
        光标位置: 13
        时间: 14:35:22
  [2]   内容: Hello, World! This is a demo.
        光标位置: 29
        时间: 14:35:22
  [3]   内容: Hello, World! This is a 
        光标位置: 24
        时间: 14:35:22
  [4]   内容: Hello, World! This is a memo pattern example.
        光标位置: 45
        时间: 14:35:22

重做栈 (0 个状态):

=== 撤销操作 ===

撤销到上一步:
当前文档:
  "Hello, World! This is a "
  光标位置: 24 (字符: ' ')
  长度: 24 字符

再撤销一步:
当前文档:
  "Hello, World! This is a demo."
  光标位置: 29 (字符: ' ')
  长度: 29 字符

再撤销一步:
当前文档:
  "Hello, World!"
  光标位置: 13 (字符: ' ')
  长度: 13 字符

=== 恢复操作 ===

恢复一步:
当前文档:
  "Hello, World! This is a demo."
  光标位置: 29 (字符: ' ')
  长度: 29 字符

再恢复一步:
当前文档:
  "Hello, World! This is a "
  光标位置: 24 (字符: ' ')
  长度: 24 字符

再恢复一步:
当前文档:
  "Hello, World! This is a memo pattern example."
  光标位置: 45 (字符: ' ')
  长度: 45 字符

=== 测试连续撤销 ===

撤销:
当前文档:
  "Hello, World! This is a "
  光标位置: 24 (字符: ' ')
  长度: 24 字符

撤销:
当前文档:
  "Hello, World! This is a demo."
  光标位置: 29 (字符: ' ')
  长度: 29 字符

撤销:
当前文档:
  "Hello, World!"
  光标位置: 13 (字符: ' ')
  长度: 13 字符

撤销:
当前文档:
  "Hello, "
  光标位置: 7 (字符: ' ')
  长度: 7 字符

撤销:
当前文档:
  ""
  光标位置: 0 (字符: ' ')
  长度: 0 字符

=== 测试连续恢复 ===

恢复:
当前文档:
  "Hello, "
  光标位置: 7 (字符: ' ')
  长度: 7 字符

恢复:
当前文档:
  "Hello, World!"
  光标位置: 13 (字符: ' ')
  长度: 13 字符

恢复:
当前文档:
  "Hello, World! This is a demo."
  光标位置: 29 (字符: ' ')
  长度: 29 字符

恢复:
当前文档:
  "Hello, World! This is a "
  光标位置: 24 (字符: ' ')
  长度: 24 字符

恢复:
当前文档:
  "Hello, World! This is a memo pattern example."
  光标位置: 45 (字符: ' ')
  长度: 45 字符

备忘录模式的核心结构

复制代码
Originator(发起人) → Memento(备忘录) ← Caretaker(管理者)
      ↑                      保存状态                      ↑
      |                         |                        |
  创建备忘录 ←-------------- 获取状态 → 保存/恢复备忘录

关键特征:

  • 状态封装:备忘录封装了发起人的内部状态
  • 状态隔离:只有发起人可以访问备忘录的完整状态
  • 历史管理:管理者负责保存和恢复备忘录,但不操作内容
  • 状态回滚:支持恢复到任意历史状态

备忘录模式的两种实现方式

1. 白箱实现(宽接口)

java 复制代码
// 备忘录对外公开所有方法
public class Memento {
    public String getState() { return state; }
    public void setState(String state) { this.state = state; }
}
// 管理者可以直接操作备忘录内容

2. 黑箱实现(窄接口)(推荐)

java 复制代码
// 备忘录接口不暴露状态操作方法
interface Memento {
    // 只有包访问权限的方法
}

// 发起人内部类实现备忘录
class Originator {
    private class ConcreteMemento implements Memento {
        private String state;
        private ConcreteMemento(String state) { this.state = state; }
        private String getState() { return state; }
    }
}
// 管理者只能保存备忘录对象,不能访问内容

适用场景(大白话版)

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

  1. 需要撤销/恢复功能

    java 复制代码
    // 文本编辑器、绘图软件、IDE
    // 任何需要Ctrl+Z/Ctrl+Y功能的软件
  2. 需要保存历史状态

    java 复制代码
    // 游戏存档、配置管理、工作流状态保存
    // 数据库事务回滚
  3. 需要快照功能

    java 复制代码
    // 虚拟机快照、系统还原点
    // 数据备份和恢复
  4. 需要状态比较

    java 复制代码
    // 文档版本比较
    // 代码差异对比

不适合的场景:

  1. 状态很小或简单:如果状态很简单,直接保存即可
  2. 状态变化非常频繁:频繁保存可能消耗大量内存
  3. 状态包含外部资源:如文件句柄、网络连接等
  4. 需要完全的历史记录:可以使用专门的历史数据库

优缺点

优点:

  • 状态封装:封装了对象状态,保护内部数据
  • 易于恢复:可以轻松恢复到任意历史状态
  • 简化发起人:发起人不需要管理历史状态
  • 符合单一职责:状态保存和业务逻辑分离

缺点:

  • 内存消耗:保存大量状态可能消耗大量内存
  • 性能开销:保存和恢复状态需要时间
  • 实现复杂:需要仔细设计状态保存策略
  • 可能暴露细节:如果不小心,可能暴露内部状态

优化技巧

1. 增量保存

java 复制代码
// 只保存变化的部分,而不是完整状态
class IncrementalMemento {
    private String changedPart;
    private int position;
    // 而不是保存整个文档
}

2. 压缩存储

java 复制代码
// 压缩保存的状态数据
class CompressedMemento {
    private byte[] compressedState;
    
    public CompressedMemento(String state) {
        this.compressedState = compress(state);
    }
}

3. 外部存储

java 复制代码
// 将备忘录保存到文件或数据库
class PersistentMemento {
    public void saveToFile(String filename) { ... }
    public static Memento loadFromFile(String filename) { ... }
}

4. 懒保存

java 复制代码
// 只在需要时保存,而不是每次变化都保存
class LazySaveManager {
    private Timer saveTimer;
    // 每5秒自动保存一次,而不是每次按键都保存
}

实际应用案例

1. 浏览器历史记录

java 复制代码
// 浏览器的前进/后退功能
public class Browser {
    private Stack<PageState> backStack = new Stack<>();
    private Stack<PageState> forwardStack = new Stack<>();
    
    public void goBack() {
        if (!backStack.isEmpty()) {
            PageState current = saveCurrentPage();
            forwardStack.push(current);
            PageState previous = backStack.pop();
            restorePage(previous);
        }
    }
}

2. 数据库事务

java 复制代码
// 数据库事务的回滚机制
Connection conn = database.getConnection();
conn.setAutoCommit(false); // 开始事务

try {
    // 执行一系列操作
    conn.commit(); // 提交
} catch (SQLException e) {
    conn.rollback(); // 回滚到事务开始的状态
}

3. 虚拟机快照

java 复制代码
// 虚拟机的快照功能
class VirtualMachine {
    public Snapshot createSnapshot() {
        return new Snapshot(
            memoryState.clone(),
            diskState.clone(),
            cpuState.clone()
        );
    }
    
    public void restoreSnapshot(Snapshot snapshot) {
        // 恢复到快照状态
    }
}

4. 游戏状态保存

java 复制代码
// 游戏的自动保存和手动保存
class GameStateManager {
    private List<SaveGame> saves = new ArrayList<>();
    
    public void autoSave(GameState state) {
        // 每10分钟自动保存
    }
    
    public void quickSave(GameState state) {
        // 玩家手动快速保存
    }
}

与命令模式的区别

模式 主要目的 状态保存方式
命令模式 封装请求为对象 命令对象包含执行所需信息
备忘录模式 保存对象状态 专门的状态对象

通常结合使用

java 复制代码
// 命令模式负责执行操作
// 备忘录模式负责保存执行前的状态,用于撤销
class CommandWithUndo implements Command {
    private Memento beforeState;
    
    public void execute() {
        beforeState = receiver.saveState(); // 保存状态
        receiver.doSomething(); // 执行操作
    }
    
    public void undo() {
        receiver.restoreState(beforeState); // 恢复状态
    }
}

总结

备忘录模式就是:

  • 时光机:可以回到过去任何一个时间点
  • 后悔药:做了错事可以回到之前的状态
  • 存档点:游戏玩到一半可以保存,下次继续
  • 版本控制:代码可以回到任意历史版本

核心口诀:

对象状态需保存,

时光倒流有可能。

备忘录里存快照,

随时恢复真轻松!

就像现实中的:

  • 🎮 游戏存档:打BOSS前先存个档
  • 📝 撤销按钮:写错了按Ctrl+Z
  • 📷 照片备份:手机照片自动备份到云端
  • 💾 系统还原:电脑出问题恢复到昨天状态

记住:当你需要保存对象状态,并且可能需要恢复到该状态时,考虑使用备忘录模式!

相关推荐
未来之窗软件服务4 小时前
自建开发工具IDE(七)数据库集群智能升级东方仙盟数据库同化,五行八卦排序+游戏修仙,精准补齐差异还能圆武侠梦—东方仙盟筑基期
数据库·游戏·oracle·仙盟创梦ide·东方仙盟·东方仙盟架构·东方仙盟商业开发
晨非辰4 小时前
基于Win32 API控制台的贪吃蛇游戏:从设计到C语言实现详解
c语言·c++·人工智能·后端·python·深度学习·游戏
wanhengidc6 小时前
物理服务器与云服务器的不同之处
运维·服务器·网络·游戏
AA陈超21 小时前
虚幻引擎5 GAS开发俯视角RPG游戏 P07-19.发送鼠标光标数据
c++·笔记·学习·游戏·ue5·虚幻引擎
毕设源码-钟学长1 天前
【开题答辩全过程】以 基于Vue NodeJs的在线游戏平台的设计与实现为例,包含答辩的问题和答案
游戏
wanhengidc2 天前
巨椰 云手机 云游戏稳定运行
运维·服务器·arm开发·游戏·云计算
_大学牲2 天前
Flutter 勇闯2D像素游戏之路(四):与哥布林战斗的滑步魔法师
flutter·游戏·游戏开发
da_vinci_x2 天前
Substance 3D Painter 进阶:手绘“掉漆”太累?用 Anchor Point 让材质“活”过来
游戏·3d·aigc·材质·设计师·技术美术·游戏美术
DoomGT2 天前
Audio - UE5中的音效播放重启问题
游戏·ue5·游戏引擎·虚幻·虚幻引擎