0%

备忘录模式

备忘录模式(Memento Pattern):对象状态的快照与恢复

备忘录模式是行为型设计模式的一种,核心思想是在不破坏对象封装性的前提下,捕获对象的内部状态并保存,以便后续将对象恢复到该状态。这种模式就像 “游戏存档”—— 玩家可在关键时刻保存进度,后续可随时加载存档回到之前的状态,核心功能是 “状态的保存与恢复”。

备忘录模式的核心结构

备忘录模式

备忘录模式通过三个核心角色实现状态的安全存储与恢复,分工明确且保障封装性:

原发器(Originator)

  • 需要被保存状态的对象,负责创建备忘录(记录当前状态)和从备忘录恢复状态。
  • 示例:GameRole(游戏角色,需保存生命值、魔法值等状态)。

备忘录(Memento)

  • 存储原发器状态的对象,通常由原发器创建,包含原发器的部分或全部内部状态。
  • 备忘录的访问权限严格控制:仅允许原发器读写其状态,其他对象无法修改(保障封装性)。
  • 示例:GameRoleMemento(存储游戏角色的生命值、魔法值)。

管理者(Caretaker)

  • 负责管理备忘录的对象,仅存储备忘录但不查看或修改其内容,相当于 “存档管理器”。
  • 示例:GameSaveManager(保存多个游戏存档,提供存档和读档接口)。

代码实现示例

以 “游戏角色状态存档” 为例,展示备忘录模式的实现:游戏角色可保存当前状态(生命值、魔法值),并在需要时恢复。

备忘录(Memento)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 备忘录:存储游戏角色状态(私有内部类,仅允许原发器访问)
public class GameRole {
// 内部备忘录类,封装角色状态
private static class Memento {
private int hp; // 生命值
private int mp; // 魔法值

public Memento(int hp, int mp) {
this.hp = hp;
this.mp = mp;
}

// 仅允许原发器访问状态
private int getHp() { return hp; }
private int getMp() { return mp; }
}

// 原发器的内部状态
private int hp;
private int mp;

public GameRole(int hp, int mp) {
this.hp = hp;
this.mp = mp;
}

// 创建备忘录(保存当前状态)
public Memento saveState() {
return new Memento(hp, mp);
}

// 从备忘录恢复状态
public void restoreState(Memento memento) {
this.hp = memento.getHp();
this.mp = memento.getMp();
}

// 其他业务方法(如战斗减血)
public void fight() {
hp -= 20;
mp -= 10;
System.out.printf("战斗后:生命值=%d,魔法值=%d%n", hp, mp);
}

// 显示当前状态
public void showState() {
System.out.printf("当前状态:生命值=%d,魔法值=%d%n", hp, mp);
}
}

管理者(Caretaker)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 管理者:管理游戏存档(备忘录)
public class GameSaveManager {
private GameRole.Memento save; // 存储单个存档(可扩展为列表保存多个存档)

// 保存存档
public void saveMemento(GameRole.Memento memento) {
this.save = memento;
System.out.println("已保存存档");
}

// 获取存档
public GameRole.Memento getMemento() {
System.out.println("读取存档");
return save;
}
}

客户端使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MementoDemo {
public static void main(String[] args) {
// 创建游戏角色(原发器)
GameRole role = new GameRole(100, 80);
role.showState(); // 当前状态:生命值=100,魔法值=80

// 创建管理者(存档管理器)
GameSaveManager saveManager = new GameSaveManager();

// 保存状态(存档)
saveManager.saveMemento(role.saveState());

// 进行战斗(状态改变)
role.fight(); // 战斗后:生命值=80,魔法值=70
role.showState(); // 当前状态:生命值=80,魔法值=70

// 恢复到之前的状态(读档)
role.restoreState(saveManager.getMemento());
role.showState(); // 当前状态:生命值=100,魔法值=80
}
}
输出结果
1
2
3
4
5
6
当前状态:生命值=100,魔法值=80
已保存存档
战斗后:生命值=80,魔法值=70
当前状态:生命值=80,魔法值=70
读取存档
当前状态:生命值=100,魔法值=80

备忘录模式的核心优势

  1. 封装性保障
    备忘录的状态仅允许原发器访问(如示例中MementoGameRole的私有内部类),其他对象(如管理者)无法修改,确保状态的安全性。
  2. 状态快照与恢复
    可在任意时刻保存对象状态,需要时快速恢复,适合需要 “回滚” 操作的场景(如游戏存档、事务回滚)。
  3. 原发器与状态存储分离
    原发器无需关心状态如何存储,管理者负责备忘录的管理,符合单一职责原则。

适用场景

  1. 需要回滚操作的场景
    • 游戏存档:保存 / 加载游戏进度。
    • 文本编辑器的撤销功能:保存每次编辑前的状态,支持撤销。
    • 数据库事务:事务提交前保存状态,失败时回滚。
  2. 需保存对象部分或全部状态
    当对象状态复杂且需频繁保存 / 恢复(如配置项修改、表单填写),备忘录模式可简化状态管理。
  3. 避免暴露对象内部状态
    不希望通过 getter 方法暴露内部状态(破坏封装),可通过备忘录间接保存状态。

优缺点分析

优点

  • 高封装性:状态存储与恢复不破坏原发器的封装,内部状态不被外部访问。
  • 灵活的状态管理:可保存多个历史状态(如管理者存储备忘录列表),支持多版本恢复。
  • 简化原发器职责:原发器无需自行管理状态历史,由管理者统一处理。

缺点

  • 资源消耗:若对象状态大或保存频繁,备忘录会占用较多内存(如频繁存档的大型游戏)。
  • 状态一致性风险:若原发器状态包含引用类型,备忘录保存的是引用(浅拷贝),可能导致恢复后状态不一致(需深拷贝解决)。

经典应用案例

  1. 文本编辑器的撤销功能
    每次编辑前保存文档状态到备忘录,撤销时从备忘录恢复,如WordCtrl+Z
  2. 数据库事务管理器
    事务执行前保存数据状态,若事务失败,通过备忘录回滚到初始状态。
  3. 配置中心的版本管理
    保存配置项的历史版本(备忘录),支持回滚到之前的配置。
  4. 状态机的状态回溯
    状态机在状态转换时保存历史状态,便于回溯或重新执行。

总结

备忘录模式通过原发器、备忘录和管理者的协作,实现了对象状态的安全保存与恢复,核心是 “封装状态、分离管理”。它特别适合需要回滚操作或状态快照的场景,既能保障对象封装性,又能灵活管理状态历史。使用时需注意平衡状态保存的粒度与资源消耗,避免因频繁保存大对象导致性能问题

欢迎关注我的其它发布渠道