在不破坏对象的封装性的前提下,在对象之外捕获并保存该对象内部的状态,以便日后对象使用或对象恢复。

定义

备忘录模式,也叫快照(Snapshot)模式,英文翻译是 Memento Design Pattern。在 GoF 的《设计模式》一书中,备忘录模式是这么定义的:

Captures and externalizes an object’s internal state so that it can be restored later, all without violating encapsulation. 在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。

结构

主要角色:

  1. 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
  2. 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
  3. 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。

结构图:
image.png

应用

文本编辑撤销

  1. public class InputText {
  2. private StringBuilder text = new StringBuilder();
  3. public String getText() {
  4. return text.toString();
  5. }
  6. public void append(String input) {
  7. text.append(input);
  8. }
  9. public Snapshot createSnapshot() {
  10. return new Snapshot(text.toString());
  11. }
  12. public void restoreSnapshot(Snapshot snapshot) {
  13. this.text.replace(0, this.text.length(), snapshot.getText());
  14. }
  15. }
  16. public class Snapshot {
  17. private String text;
  18. public Snapshot(String text) {
  19. this.text = text;
  20. }
  21. public String getText() {
  22. return this.text;
  23. }
  24. }
  25. public class SnapshotHolder {
  26. private Stack<Snapshot> snapshots = new Stack<>();
  27. public Snapshot popSnapshot() {
  28. return snapshots.pop();
  29. }
  30. public void pushSnapshot(Snapshot snapshot) {
  31. snapshots.push(snapshot);
  32. }
  33. }
  34. public class ApplicationMain {
  35. public static void main(String[] args) {
  36. InputText inputText = new InputText();
  37. SnapshotHolder snapshotsHolder = new SnapshotHolder();
  38. Scanner scanner = new Scanner(System.in);
  39. while (scanner.hasNext()) {
  40. String input = scanner.next();
  41. if (input.equals(":list")) {
  42. System.out.println(inputText.toString());
  43. } else if (input.equals(":undo")) {
  44. Snapshot snapshot = snapshotsHolder.popSnapshot();
  45. inputText.restoreSnapshot(snapshot);
  46. } else {
  47. snapshotsHolder.pushSnapshot(inputText.createSnapshot());
  48. inputText.append(input);
  49. }
  50. }
  51. }
  52. }

优点

  • 提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
  • 实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
  • 简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。

缺点

资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。

扩展

全量备份,增量备份

当我们需要恢复到某一时间点的备份的时候,如果这一时间点有做全量备份,我们直接拿来恢复就可以了。如果这一时间点没有对应的全量备份,我们就先找到最近的一次全量备份,然后用它来恢复,之后执行此次全量备份跟这一时间点之间的所有增量备份,也就是对应的操作或者数据变动。
实际场景中一般将全量备份和增量备份相结合,低频全量备份,高频增量备份,两者结合来做恢复。