Java案例中的备忘录模式怎么用?

wen java案例 5

本文目录导读:

Java案例中的备忘录模式怎么用?

  1. Java备忘录模式详解
  2. 实际应用场景
  3. 注意事项

Java备忘录模式详解

备忘录模式(Memento Pattern)用于在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便将来恢复。

核心角色

  1. 发起人(Originator):需要保存状态的对象
  2. 备忘录(Memento):存储发起人内部状态
  3. 管理者(Caretaker):负责保存和管理备忘录

经典案例:游戏存档系统

// 1. 备忘录类 - 存储游戏状态
class GameMemento {
    private int level;
    private int health;
    private String position;
    private int score;
    public GameMemento(int level, int health, String position, int score) {
        this.level = level;
        this.health = health;
        this.position = position;
        this.score = score;
    }
    // 只提供getter方法,不提供setter(保持不可变性)
    public int getLevel() { return level; }
    public int getHealth() { return health; }
    public String getPosition() { return position; }
    public int getScore() { return score; }
}
// 2. 发起人类 - 游戏角色
class GameCharacter implements Cloneable {
    private int level;
    private int health;
    private String position;
    private int score;
    public GameCharacter() {
        this.level = 1;
        this.health = 100;
        this.position = "起点";
        this.score = 0;
    }
    // 创建备忘录(保存状态)
    public GameMemento save() {
        return new GameMemento(level, health, position, score);
    }
    // 从备忘录恢复状态
    public void restore(GameMemento memento) {
        if (memento == null) {
            throw new IllegalArgumentException("备忘录不能为空");
        }
        this.level = memento.getLevel();
        this.health = memento.getHealth();
        this.position = memento.getPosition();
        this.score = memento.getScore();
    }
    // 游戏操作
    public void fight(int damage) {
        this.health -= damage;
        System.out.println("战斗受伤,当前生命值: " + health);
    }
    public void moveTo(String newPosition, int scoreGain) {
        this.position = newPosition;
        this.score += scoreGain;
        System.out.println("移动到: " + position + ", 得分: " + score);
    }
    public void levelUp() {
        this.level++;
        this.health = 100;
        System.out.println("升级到等级: " + level + ", 生命恢复满");
    }
    @Override
    public String toString() {
        return String.format("等级:%d, 生命:%d, 位置:%s, 分数:%d", 
                              level, health, position, score);
    }
}
// 3. 管理者类 - 存档管理器
class GameSaveManager {
    private Stack<GameMemento> saveSlots = new Stack<>();
    private int maxSlots;
    public GameSaveManager(int maxSlots) {
        this.maxSlots = maxSlots;
    }
    // 存档(压入栈中)
    public void saveGame(GameCharacter character) {
        if (saveSlots.size() >= maxSlots) {
            System.out.println("存档已满,覆盖最早的存档");
            saveSlots.remove(0);
        }
        saveSlots.push(character.save());
        System.out.println("游戏已保存,当前存档数: " + saveSlots.size());
    }
    // 读档(从栈顶取出)
    public void loadGame(GameCharacter character) {
        if (saveSlots.isEmpty()) {
            System.out.println("没有可用的存档");
            return;
        }
        GameMemento memento = saveSlots.pop();
        character.restore(memento);
        System.out.println("游戏已加载");
    }
    // 查看所有存档
    public void listSaves() {
        System.out.println("当前存档数量: " + saveSlots.size());
        // 注意:这里无法查看备忘录的具体内容,因为备忘录是私有的
    }
}
// 4. 客户端代码
public class MementoPatternDemo {
    public static void main(String[] args) {
        GameCharacter player = new GameCharacter();
        GameSaveManager saveManager = new GameSaveManager(3);
        System.out.println("初始状态:");
        System.out.println(player);
        // 第一次存档
        saveManager.saveGame(player);
        // 游戏进行
        player.moveTo("森林", 50);
        player.fight(30);
        System.out.println("战斗后: " + player);
        // 第二次存档
        saveManager.saveGame(player);
        // 继续游戏
        player.levelUp();
        player.moveTo("城堡", 100);
        player.fight(40);
        System.out.println("继续游戏后: " + player);
        // 读档(回到第二次存档)
        System.out.println("\n读档操作:");
        saveManager.loadGame(player);
        System.out.println("读档后: " + player);
        // 再读档(回到第一次存档)
        System.out.println("\n再次读档:");
        saveManager.loadGame(player);
        System.out.println("读档后: " + player);
    }
}

输出示例

初始状态:
等级:1, 生命:100, 位置:起点, 分数:0
游戏已保存,当前存档数: 1
移动到: 森林, 得分: 50
战斗受伤,当前生命值: 70
战斗后: 等级:1, 生命:70, 位置:森林, 分数:50
游戏已保存,当前存档数: 2
升级到等级: 2, 生命恢复满
移动到: 城堡, 得分: 150
战斗受伤,当前生命值: 60
继续游戏后: 等级:2, 生命:60, 位置:城堡, 分数:150
读档操作:
游戏已加载
读档后: 等级:1, 生命:70, 位置:森林, 分数:50
再次读档:
游戏已加载
读档后: 等级:1, 生命:100, 位置:起点, 分数:0

实际应用场景

文本编辑器撤销功能

class TextEditor {
    private StringBuilder content;
    private Stack<EditorMemento> history;
    public void write(String text) {
        history.push(save());
        content.append(text);
    }
    public void undo() {
        if (!history.isEmpty()) {
            restore(history.pop());
        }
    }
}

数据库事务回滚

class DatabaseTransaction {
    private Map<String, Object> state;
    private List<TransactionMemento> savepoints;
    public void begin() {
        savepoints.clear();
        savepoints.add(save()); // 保存初始状态
    }
    public void rollback() {
        if (!savepoints.isEmpty()) {
            restore(savepoints.get(0));
        }
    }
}

注意事项

优点

  • 状态还原:可以轻松实现撤销/重做功能
  • 封装性:备忘录对象不暴露内部状态给外部
  • 简化发起人:发起人不需管理历史状态

缺点

  • 资源消耗:如果状态很大,频繁保存会消耗内存
  • 实现复杂度:需要额外的备忘录和管理者类
  • 语言限制:Java中需要额外处理Object的克隆

最佳实践

  • 使用浅拷贝/深拷贝:确保状态对象的完整复制
  • 管理内存:限制存档数量或使用压缩存储
  • 序列化支持:对于复杂的对象,考虑使用序列化

与其他模式结合

  • 命令模式:用于实现更复杂的撤销/重做
  • 原型模式:通过克隆简化状态保存

备忘录模式非常适合需要提供撤销功能的应用,如文本编辑器、游戏存档、绘图软件等场景。

抱歉,评论功能暂时关闭!