模式定义

备忘录模式是一种行为型设计模式,它在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。备忘录模式又叫做快照模式(Snapshot Pattern)或Token模式,属于对象行为型模式。

模式结构

graph TD A[Originator] --> B[Memento] C[Caretaker] --> B

实现方式

基础实现

定义备忘录类、发起人类和管理者类。

// 备忘录类
class Memento {
    private String state;
    
    public Memento(String state) {
        this.state = state;
    }
    
    public String getState() {
        return state;
    }
}

// 发起人类
class Originator {
    private String state;
    
    public void setState(String state) {
        this.state = state;
    }
    
    public String getState() {
        return state;
    }
    
    // 创建备忘录
    public Memento saveStateToMemento() {
        return new Memento(state);
    }
    
    // 从备忘录恢复状态
    public void getStateFromMemento(Memento memento) {
        state = memento.getState();
    }
}

// 管理者类
class CareTaker {
    private List mementoList = new ArrayList<>();
    
    public void add(Memento state) {
        mementoList.add(state);
    }
    
    public Memento get(int index) {
        return mementoList.get(index);
    }
    
    public int size() {
        return mementoList.size();
    }
}

使用示例

public class MementoPatternDemo {
    public static void main(String[] args) {
        Originator originator = new Originator();
        CareTaker careTaker = new CareTaker();
        
        // 设置状态并保存
        originator.setState("状态 #1");
        careTaker.add(originator.saveStateToMemento());
        System.out.println("当前状态: " + originator.getState());
        
        originator.setState("状态 #2");
        careTaker.add(originator.saveStateToMemento());
        System.out.println("当前状态: " + originator.getState());
        
        originator.setState("状态 #3");
        careTaker.add(originator.saveStateToMemento());
        System.out.println("当前状态: " + originator.getState());
        
        // 恢复到之前的状态
        System.out.println("\n=== 恢复状态 ===");
        originator.getStateFromMemento(careTaker.get(1));
        System.out.println("恢复到状态 #2: " + originator.getState());
        
        originator.getStateFromMemento(careTaker.get(0));
        System.out.println("恢复到状态 #1: " + originator.getState());
    }
}

经典案例

1. 文本编辑器的撤销功能

文本编辑器中的撤销功能是备忘录模式的经典应用:

// 文本编辑器状态备忘录
class TextMemento {
    private String text;
    private int cursorPosition;
    private LocalDateTime timestamp;
    
    public TextMemento(String text, int cursorPosition) {
        this.text = text;
        this.cursorPosition = cursorPosition;
        this.timestamp = LocalDateTime.now();
    }
    
    public String getText() {
        return text;
    }
    
    public int getCursorPosition() {
        return cursorPosition;
    }
    
    public LocalDateTime getTimestamp() {
        return timestamp;
    }
    
    @Override
    public String toString() {
        return "状态 [时间=" + timestamp + ", 文本长度=" + text.length() + "]";
    }
}

// 文本编辑器(发起人)
class TextEditor {
    private String text = "";
    private int cursorPosition = 0;
    
    public void setText(String text) {
        this.text = text;
    }
    
    public String getText() {
        return text;
    }
    
    public void setCursorPosition(int cursorPosition) {
        this.cursorPosition = cursorPosition;
    }
    
    public int getCursorPosition() {
        return cursorPosition;
    }
    
    // 创建备忘录
    public TextMemento save() {
        System.out.println("保存状态: " + text.substring(0, Math.min(20, text.length())) + "...");
        return new TextMemento(text, cursorPosition);
    }
    
    // 恢复状态
    public void restore(TextMemento memento) {
        this.text = memento.getText();
        this.cursorPosition = memento.getCursorPosition();
        System.out.println("恢复状态: " + text.substring(0, Math.min(20, text.length())) + "...");
    }
    
    // 模拟编辑操作
    public void type(String newText) {
        this.text += newText;
        this.cursorPosition = this.text.length();
    }
    
    public void delete(int count) {
        if (count <= text.length()) {
            this.text = text.substring(0, text.length() - count);
            this.cursorPosition = this.text.length();
        }
    }
}

// 历史记录管理器(管理者)
class HistoryManager {
    private Stack history = new Stack<>();
    private int maxSize;
    
    public HistoryManager(int maxSize) {
        this.maxSize = maxSize;
    }
    
    public void save(TextMemento memento) {
        if (history.size() >= maxSize) {
            history.remove(0); // 移除最旧的状态
        }
        history.push(memento);
    }
    
    public TextMemento undo() {
        if (!history.isEmpty()) {
            return history.pop();
        }
        return null;
    }
    
    public boolean canUndo() {
        return !history.isEmpty();
    }
    
    public void showHistory() {
        System.out.println("=== 历史记录 ===");
        for (int i = 0; i < history.size(); i++) {
            System.out.println((i + 1) + ". " + history.get(i));
        }
    }
}

// 使用示例
public class TextEditorDemo {
    public static void main(String[] args) {
        TextEditor editor = new TextEditor();
        HistoryManager history = new HistoryManager(5);
        
        // 编辑文本
        editor.type("Hello");
        history.save(editor.save());
        
        editor.type(" World");
        history.save(editor.save());
        
        editor.type("!");
        history.save(editor.save());
        
        System.out.println("当前文本: " + editor.getText());
        history.showHistory();
        
        // 撤销操作
        System.out.println("\n=== 执行撤销 ===");
        if (history.canUndo()) {
            editor.restore(history.undo());
            System.out.println("撤销后文本: " + editor.getText());
        }
        
        if (history.canUndo()) {
            editor.restore(history.undo());
            System.out.println("再次撤销后文本: " + editor.getText());
        }
    }
}

2. 游戏存档系统

游戏中的存档和读档功能也是备忘录模式的应用:

// 游戏状态备忘录
class GameStateMemento {
    private int level;
    private int score;
    private int health;
    private List inventory;
    private LocalDateTime saveTime;
    
    public GameStateMemento(int level, int score, int health, List inventory) {
        this.level = level;
        this.score = score;
        this.health = health;
        this.inventory = new ArrayList<>(inventory);
        this.saveTime = LocalDateTime.now();
    }
    
    // Getters
    public int getLevel() { return level; }
    public int getScore() { return score; }
    public int getHealth() { return health; }
    public List getInventory() { return new ArrayList<>(inventory); }
    public LocalDateTime getSaveTime() { return saveTime; }
    
    @Override
    public String toString() {
        return String.format("存档 [时间=%s, 关卡=%d, 分数=%d, 生命=%d, 物品数=%d]", 
                           saveTime, level, score, health, inventory.size());
    }
}

// 游戏角色(发起人)
class GameCharacter {
    private int level = 1;
    private int score = 0;
    private int health = 100;
    private List inventory = new ArrayList<>();
    
    public void playLevel(int level) {
        this.level = level;
        this.score += level * 100;
        System.out.println("完成第 " + level + " 关,获得 " + (level * 100) + " 分");
    }
    
    public void takeDamage(int damage) {
        this.health -= damage;
        if (this.health < 0) this.health = 0;
        System.out.println("受到 " + damage + " 点伤害,剩余生命: " + this.health);
    }
    
    public void heal(int amount) {
        this.health += amount;
        if (this.health > 100) this.health = 100;
        System.out.println("恢复 " + amount + " 点生命,当前生命: " + this.health);
    }
    
    public void addItem(String item) {
        inventory.add(item);
        System.out.println("获得物品: " + item);
    }
    
    // 创建存档
    public GameStateMemento saveGame() {
        System.out.println("游戏存档...");
        return new GameStateMemento(level, score, health, inventory);
    }
    
    // 读取存档
    public void loadGame(GameStateMemento memento) {
        this.level = memento.getLevel();
        this.score = memento.getScore();
        this.health = memento.getHealth();
        this.inventory = memento.getInventory();
        System.out.println("读取存档: " + memento);
    }
    
    public void showStatus() {
        System.out.println("=== 当前状态 ===");
        System.out.println("关卡: " + level);
        System.out.println("分数: " + score);
        System.out.println("生命: " + health);
        System.out.println("物品: " + inventory);
    }
}

// 存档管理器(管理者)
class SaveGameManager {
    private List saves = new ArrayList<>();
    private int maxSaves = 3;
    
    public void saveGame(GameStateMemento save) {
        if (saves.size() >= maxSaves) {
            saves.remove(0); // 删除最旧的存档
        }
        saves.add(save);
        System.out.println("存档成功: " + save);
    }
    
    public GameStateMemento loadGame(int index) {
        if (index >= 0 && index < saves.size()) {
            return saves.get(index);
        }
        return null;
    }
    
    public void showSaves() {
        System.out.println("=== 存档列表 ===");
        for (int i = 0; i < saves.size(); i++) {
            System.out.println((i + 1) + ". " + saves.get(i));
        }
    }
    
    public int getSaveCount() {
        return saves.size();
    }
}

// 使用示例
public class GameDemo {
    public static void main(String[] args) {
        GameCharacter player = new GameCharacter();
        SaveGameManager saveManager = new SaveGameManager();
        
        // 游戏进程
        player.showStatus();
        player.playLevel(1);
        player.addItem("剑");
        saveManager.saveGame(player.saveGame());
        
        player.playLevel(2);
        player.takeDamage(30);
        player.addItem("药水");
        saveManager.saveGame(player.saveGame());
        
        player.playLevel(3);
        player.heal(50);
        player.addItem("盾牌");
        saveManager.saveGame(player.saveGame());
        
        player.showStatus();
        saveManager.showSaves();
        
        // 读取存档
        System.out.println("\n=== 读取第一个存档 ===");
        GameStateMemento save = saveManager.loadGame(0);
        if (save != null) {
            player.loadGame(save);
            player.showStatus();
        }
    }
}

应用场景

优缺点

优点

  • 保护封装性:备忘录模式将对象的内部状态保存在备忘录对象中,避免暴露对象的内部实现
  • 简化发起人类:发起人不需要管理和保存其内部状态的各个版本
  • 状态恢复机制:提供了一种状态恢复的机制,可以方便地恢复到历史状态
  • 符合单一职责原则:发起人负责状态的创建和恢复,管理者负责状态的保存

缺点

  • 消耗资源:如果需要保存的内部状态信息过多或者保存频率过高,会消耗大量内存
  • 可能破坏封装性:为了保存和恢复对象状态,需要暴露一些内部状态给其他对象
  • 增加系统复杂性:引入备忘录模式会增加系统的复杂性