行为型-备忘录模式

1. 项目结构

  1. 项目结构
bash 复制代码
	memento-pattern-demo/
	├── pom.xml
	├── src/
	│   └── main/
	│       └── java/
	│           └── com/
	│               └── example/
	│                   └── memento/
	│                       ├── Main.java
	│                       ├── TextEditor.java
	│                       ├── TextEditorMemento.java
	│                       ├── TextEditorHistory.java
	│                       └── game/
	│                           ├── GameCharacter.java
	│                           ├── CharacterMemento.java
	│                           └── CharacterCaretaker.java
  1. Maven配置文件 (pom.xml)
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>memento-pattern-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>Memento Pattern Demo</name>
    <description>A demonstration of the Memento design pattern</description>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2. 代码实现

  1. 示例1: 文本编辑器应用

    java 复制代码
    package com.example.memento;
    
    /**
     * 备忘录接口 - 定义备忘录的通用行为
     */
    public interface Memento {
        String getName();
        String getDate();
    }
    
    /**
     * 文本编辑器备忘录 - 存储文本编辑器的状态
     */
    public class TextEditorMemento implements Memento {
        private final String content;
        private final String date;
        private final String name;
    
        public TextEditorMemento(String content, String name) {
            this.content = content;
            this.name = name;
            this.date = java.time.LocalDateTime.now().toString();
        }
    
        public String getContent() {
            return content;
        }
    
        @Override
        public String getName() {
            return name;
        }
    
        @Override
        public String getDate() {
            return date;
        }
    
        @Override
        public String toString() {
            return "备忘录: " + name + " (创建于: " + date + ")";
        }
    }
    
    /**
     * 原发器 - 文本编辑器
     */
    public class TextEditor {
        private String content;
        private String currentFont;
        private int fontSize;
    
        public TextEditor() {
            this.content = "";
            this.currentFont = "Arial";
            this.fontSize = 12;
        }
    
        public void type(String text) {
            this.content += text;
            System.out.println("输入内容: " + text);
            System.out.println("当前内容: " + content);
        }
    
        public void setFont(String font, int size) {
            this.currentFont = font;
            this.fontSize = size;
            System.out.println("字体设置: " + font + ", 大小: " + size);
        }
    
        public void printStatus() {
            System.out.println("=== 当前编辑器状态 ===");
            System.out.println("内容: " + content);
            System.out.println("字体: " + currentFont);
            System.out.println("字号: " + fontSize);
            System.out.println("==================");
        }
    
        // 创建备忘录
        public TextEditorMemento save(String snapshotName) {
            System.out.println("创建快照: " + snapshotName);
            return new TextEditorMemento(content + " [字体: " + currentFont + ", 大小: " + fontSize + "]", snapshotName);
        }
    
        // 从备忘录恢复
        public void restore(TextEditorMemento memento) {
            this.content = memento.getContent();
            System.out.println("恢复快照: " + memento.getName());
            System.out.println("恢复后内容: " + content);
        }
    
        public String getContent() {
            return content;
        }
    }
    
    /**
     * 负责人 - 管理备忘录历史
     */
    public class TextEditorHistory {
        private final java.util.List<TextEditorMemento> mementoList = new java.util.ArrayList<>();
    
        public void add(TextEditorMemento memento) {
            mementoList.add(memento);
        }
    
        public TextEditorMemento get(int index) {
            if (index >= 0 && index < mementoList.size()) {
                return mementoList.get(index);
            }
            return null;
        }
    
        public TextEditorMemento getLast() {
            if (!mementoList.isEmpty()) {
                return mementoList.get(mementoList.size() - 1);
            }
            return null;
        }
    
        public TextEditorMemento getByName(String name) {
            for (TextEditorMemento memento : mementoList) {
                if (memento.getName().equals(name)) {
                    return memento;
                }
            }
            return null;
        }
    
        public void showHistory() {
            System.out.println("\n=== 备忘录历史 ===");
            if (mementoList.isEmpty()) {
                System.out.println("历史记录为空");
            } else {
                for (int i = 0; i < mementoList.size(); i++) {
                    System.out.println(i + ": " + mementoList.get(i));
                }
            }
            System.out.println("===============\n");
        }
    }
  2. 示例2: 游戏角色状态保存

    java 复制代码
    package com.example.memento.game;
    
    import java.io.*;
    
    /**
     * 游戏角色 - 原发器
     */
    public class GameCharacter implements Serializable {
        private String name;
        private int level;
        private int health;
        private int mana;
        private String weapon;
        private String armor;
    
        public GameCharacter(String name) {
            this.name = name;
            this.level = 1;
            this.health = 100;
            this.mana = 50;
            this.weapon = "木剑";
            this.armor = "布衣";
        }
    
        public void levelUp() {
            level++;
            health += 20;
            mana += 10;
            System.out.println(name + " 升级了! 当前等级: " + level);
        }
    
        public void equipWeapon(String weapon) {
            this.weapon = weapon;
            System.out.println(name + " 装备了: " + weapon);
        }
    
        public void equipArmor(String armor) {
            this.armor = armor;
            System.out.println(name + " 穿上了: " + armor);
        }
    
        public void takeDamage(int damage) {
            health -= damage;
            System.out.println(name + " 受到 " + damage + " 点伤害,剩余生命: " + health);
        }
    
        public void useMana(int amount) {
            mana -= amount;
            System.out.println(name + " 消耗 " + amount + " 点魔法,剩余魔法: " + mana);
        }
    
        public void printStatus() {
            System.out.println("\n=== 角色状态 ===");
            System.out.println("名称: " + name);
            System.out.println("等级: " + level);
            System.out.println("生命: " + health);
            System.out.println("魔法: " + mana);
            System.out.println("武器: " + weapon);
            System.out.println("护甲: " + armor);
            System.out.println("==============\n");
        }
    
        // 创建备忘录(使用序列化实现深拷贝)
        public CharacterMemento save(String saveName) {
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(this);
                oos.close();
                
                return new CharacterMemento(saveName, baos.toByteArray());
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }
    
        // 从备忘录恢复
        public void restore(CharacterMemento memento) {
            try {
                ByteArrayInputStream bais = new ByteArrayInputStream(memento.getState());
                ObjectInputStream ois = new ObjectInputStream(bais);
                GameCharacter savedCharacter = (GameCharacter) ois.readObject();
                ois.close();
                
                this.name = savedCharacter.name;
                this.level = savedCharacter.level;
                this.health = savedCharacter.health;
                this.mana = savedCharacter.mana;
                this.weapon = savedCharacter.weapon;
                this.armor = savedCharacter.armor;
                
                System.out.println("已加载存档: " + memento.getName());
            } catch (IOException | ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    
    /**
     * 游戏角色备忘录
     */
    public class CharacterMemento {
        private final String name;
        private final byte[] state;
        private final String date;
    
        public CharacterMemento(String name, byte[] state) {
            this.name = name;
            this.state = state;
            this.date = java.time.LocalDateTime.now().toString();
        }
    
        public String getName() {
            return name;
        }
    
        public byte[] getState() {
            return state;
        }
    
        public String getDate() {
            return date;
        }
    
        @Override
        public String toString() {
            return "存档: " + name + " (创建于: " + date + ")";
        }
    }
    
    /**
     * 游戏存档管理器
     */
    public class CharacterCaretaker {
        private final java.util.Map<String, CharacterMemento> saves = new java.util.HashMap<>();
    
        public void saveGame(GameCharacter character, String saveName) {
            CharacterMemento memento = character.save(saveName);
            if (memento != null) {
                saves.put(saveName, memento);
                System.out.println("游戏已保存: " + saveName);
            }
        }
    
        public void loadGame(GameCharacter character, String saveName) {
            CharacterMemento memento = saves.get(saveName);
            if (memento != null) {
                character.restore(memento);
            } else {
                System.out.println("存档不存在: " + saveName);
            }
        }
    
        public void deleteSave(String saveName) {
            if (saves.remove(saveName) != null) {
                System.out.println("已删除存档: " + saveName);
            } else {
                System.out.println("存档不存在: " + saveName);
            }
        }
    
        public void listSaves() {
            System.out.println("\n=== 存档列表 ===");
            if (saves.isEmpty()) {
                System.out.println("无存档");
            } else {
                saves.values().forEach(System.out::println);
            }
            System.out.println("==============\n");
        }
    }

3. 主程序演示

复制代码
```java
package com.example.memento;

import com.example.memento.game.GameCharacter;
import com.example.memento.game.CharacterCaretaker;

public class Main {
    public static void main(String[] args) {
        System.out.println("============== 备忘录模式演示 ==============\n");
        
        System.out.println("示例1: 文本编辑器");
        demonstrateTextEditor();
        
        System.out.println("\n\n示例2: 游戏角色存档");
        demonstrateGameCharacter();
        
        System.out.println("\n============== 演示结束 ==============");
    }
    
    private static void demonstrateTextEditor() {
        // 创建文本编辑器和历史记录管理器
        TextEditor editor = new TextEditor();
        TextEditorHistory history = new TextEditorHistory();
        
        // 编辑文本并创建快照
        editor.type("Hello, ");
        history.add(editor.save("初始编辑"));
        
        editor.type("World!");
        editor.setFont("Times New Roman", 14);
        history.add(editor.save("添加世界"));
        
        editor.type(" 这是备忘录模式的示例。");
        editor.setFont("Courier New", 16);
        editor.printStatus();
        
        // 显示历史
        history.showHistory();
        
        // 恢复到之前的某个状态
        System.out.println("\n--- 恢复到'初始编辑'状态 ---");
        editor.restore(history.getByName("初始编辑"));
        editor.printStatus();
        
        // 添加更多编辑
        System.out.println("\n--- 继续编辑 ---");
        editor.type("新的开始...");
        history.add(editor.save("新开始"));
        editor.printStatus();
        
        // 显示最终历史
        history.showHistory();
    }
    
    private static void demonstrateGameCharacter() {
        // 创建游戏角色和存档管理器
        GameCharacter hero = new GameCharacter("冒险者");
        CharacterCaretaker saveManager = new CharacterCaretaker();
        
        // 显示初始状态
        hero.printStatus();
        
        // 游戏进程并保存
        hero.levelUp();
        hero.equipWeapon("钢铁剑");
        hero.printStatus();
        saveManager.saveGame(hero, "初始存档");
        
        // 继续游戏
        hero.levelUp();
        hero.levelUp();
        hero.equipArmor("锁子甲");
        hero.takeDamage(30);
        hero.useMana(20);
        hero.printStatus();
        saveManager.saveGame(hero, "中期存档");
        
        // 继续游戏
        hero.levelUp();
        hero.equipWeapon("魔法剑");
        hero.takeDamage(50);
        hero.printStatus();
        saveManager.saveGame(hero, "危险状态存档");
        
        // 显示存档列表
        saveManager.listSaves();
        
        // 加载存档
        System.out.println("--- 加载'中期存档' ---");
        saveManager.loadGame(hero, "中期存档");
        hero.printStatus();
        
        // 尝试不存在的存档
        System.out.println("--- 尝试加载不存在的存档 ---");
        saveManager.loadGame(hero, "不存在存档");
        
        // 删除存档
        System.out.println("--- 删除'危险状态存档' ---");
        saveManager.deleteSave("危险状态存档");
        saveManager.listSaves();
        
        // 加载已删除的存档
        System.out.println("--- 尝试加载已删除的存档 ---");
        saveManager.loadGame(hero, "危险状态存档");
    }
}
```

3. 构建和运行

  1. 使用Maven编译:

    bash 复制代码
    mvn clean compile
  2. 运行程序:

    bash 复制代码
    mvn exec:java -Dexec.mainClass="com.example.memento.Main"
  3. 直接运行:

    bash 复制代码
    java -cp target/classes com.example.memento.Main

4. 核心概念

  • 三个核心角色:
    • Originator(原发器):需要保存状态的对象(如TextEditor、GameCharacter)
    • Memento(备忘录):存储原发器内部状态的对象
    • Caretaker(负责人):负责保存和管理备忘录,但不直接操作其内容
  • 模式优点:
    • 提供了一种状态恢复机制
    • 保持了封装边界,原发器状态不会被外部直接访问
    • 简化了原发器职责,将状态保存逻辑分离
  • 应用场景:
    • 需要保存和恢复对象状态的历史记录
    • 需要实现撤销/重做功能
    • 需要保存对象快照以供后续使用
  • 实现方式:
    • 简单实现:直接复制对象状态
    • 序列化实现:通过序列化实现深拷贝(如游戏角色示例)
    • 增量备忘录:只保存变化的部分状态
相关推荐
__万波__1 天前
二十三种设计模式(十九)--备忘录模式
java·设计模式·备忘录模式
老朱佩琪!12 天前
Unity备忘录模式
java·unity·备忘录模式
Yeniden13 天前
Deepeek用大白话讲解 --> 备忘录模式(企业级场景1,撤销重做2,状态保存3,游戏存档4)
游戏·备忘录模式
QQ 192263825 天前
三自由度汽车操纵侧翻模型仿真 有说明文档 学习资料:附带整理好的Word理论分析说明文档
备忘录模式
明洞日记1 个月前
【设计模式手册017】备忘录模式 - 对象状态保存与恢复
c++·设计模式·备忘录模式
开心香辣派小星1 个月前
23种设计模式-17备忘录模式
java·设计模式·备忘录模式
将编程培养成爱好2 个月前
C++ 设计模式《账本事故:当备份被删光那天》
开发语言·c++·设计模式·备忘录模式
紫荆鱼2 个月前
设计模式-备忘录模式(Memento)
c++·后端·设计模式·备忘录模式
o0向阳而生0o3 个月前
106、23种设计模式之备忘录模式(15/23)
设计模式·备忘录模式