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
- 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: 文本编辑器应用
javapackage 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: 游戏角色状态保存
javapackage 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. 构建和运行
-
使用Maven编译:
bashmvn clean compile -
运行程序:
bashmvn exec:java -Dexec.mainClass="com.example.memento.Main" -
直接运行:
bashjava -cp target/classes com.example.memento.Main
4. 核心概念
- 三个核心角色:
- Originator(原发器):需要保存状态的对象(如TextEditor、GameCharacter)
- Memento(备忘录):存储原发器内部状态的对象
- Caretaker(负责人):负责保存和管理备忘录,但不直接操作其内容
- 模式优点:
- 提供了一种状态恢复机制
- 保持了封装边界,原发器状态不会被外部直接访问
- 简化了原发器职责,将状态保存逻辑分离
- 应用场景:
- 需要保存和恢复对象状态的历史记录
- 需要实现撤销/重做功能
- 需要保存对象快照以供后续使用
- 实现方式:
- 简单实现:直接复制对象状态
- 序列化实现:通过序列化实现深拷贝(如游戏角色示例)
- 增量备忘录:只保存变化的部分状态