备忘录这个词汇大家应该都不陌生,我就经常使用备忘录来记录一些比较重要的或者容易遗忘的信息,与之相关的最常见的应用有许多,比如游戏存档,我们玩游戏的时候肯定有存档功能,旨在下一次登录游戏时可以从上次退出的地方继续游戏,或者对复活点进行存档,如果挂掉了则可以读取复活点的存档信息重新开始。与之相类似的就是数据库的事务回滚,或者重做日志redo log等。

  备忘录模式(Memento),在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存着这个状态。这样以后就可将该对象恢复到原先保存的状态。UML结构图如下:

  其中,Originator是发起人,负责创建一个备忘录Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态;Memento是备忘录,负责存储Originator对象的内部状态,并可防止Originator以外的其他对象访问备忘录Memento;Caretaker是管理者,负责保存好备忘录的Memento,不能对备忘录的内容进行操作或检查。

  记录当前时刻的内部状态,并负责创建和恢复备忘录数据,允许访问返回到先前状态所需的所有数据。

  1. 1 public class Originator {
  2. 2
  3. 3 private String state;
  4. 4
  5. 5 public String getState() {
  6. 6 return state;
  7. 7 }
  8. 8
  9. 9 public void setState(String state) {
  10. 10 this.state = state;
  11. 11 }
  12. 12
  13. 13 public Memento createMento() {
  14. 14 return (new Memento(state));
  15. 15 }
  16. 16
  17. 17 public void setMemento(Memento memento) {
  18. 18 state = memento.getState();
  19. 19 }
  20. 20
  21. 21 public void show() {
  22. 22 System.out.println("state = " + state);
  23. 23 }
  24. 24
  25. 25 }

  负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。

  1. 1 public class Memento {
  2. 2
  3. 3 private String state;
  4. 4
  5. 5 public Memento(String state) {
  6. 6 this.state = state;
  7. 7 }
  8. 8
  9. 9 public String getState() {
  10. 10 return state;
  11. 11 }
  12. 12
  13. 13 }

  对备忘录进行管理、保存和提供备忘录,只能将备忘录传递给其他角色。

  1. 1 public class Caretaker {
  2. 2
  3. 3 private Memento memento;
  4. 4
  5. 5 public Memento getMemento() {
  6. 6 return memento;
  7. 7 }
  8. 8
  9. 9 public void setMemento(Memento memento) {
  10. 10 this.memento = memento;
  11. 11 }
  12. 12
  13. 13 }

  下面编写一小段代码测试一下,即先将状态置为On,保存后再将状态置为Off,然后通过备忘录管理员角色恢复初始状态。

  1. 1 public class Client {
  2. 2
  3. 3 public static void main(String[] args) {
  4. 4 Originator originator = new Originator();
  5. 5 originator.setState("On"); //Originator初始状态
  6. 6 originator.show();
  7. 7
  8. 8 Caretaker caretaker = new Caretaker();
  9. 9 caretaker.setMemento(originator.createMento());
  10. 10
  11. 11 originator.setState("Off"); //Originator状态变为Off
  12. 12 originator.show();
  13. 13
  14. 14 originator.setMemento(caretaker.getMemento()); //回复初始状态
  15. 15 originator.show();
  16. 16 }
  17. 17
  18. 18 }

  运行结果如下:

  

  • 需要记录一个对象的内部状态时,为了允许用户取消不确定或者错误的操作,能够恢复到原先的状态
  • 通过一个备忘录类专门存储对象状态
  • 给用户提供了一种可以恢复状态的机制,可以使用能够比较方便地回到某个历史的状态
  • 实现了信息的封装,使得用户不需要关心状态的保存细节
  • 消耗资源
  • 需要保存和恢复数据的相关场景
  • 提供一个可回滚的操作,如ctrl+z、浏览器回退按钮、Backspace键等
  • 需要监控的副本场景
  • 游戏存档
  • ctrl+z键、浏览器回退键等(撤销/还原)
  • 棋盘类游戏的悔棋
  • 数据库事务的回滚
  • 为了符合迪米特法则,需要有一个管理备忘录的类
  • 不要在频繁建立备份的场景中使用备忘录模式。为了节约内存,可使用原型模式+备忘录模式

  下面以游戏存档为例,看一下如何用备忘录模式实现。UML图如下:

  简单记录了游戏角色的生命力、攻击力、防御力,通过saveState()方法来保存当前状态,通过recoveryState()方法来恢复角色状态。

  1. 1 public class GameRole {
  2. 2
  3. 3 private int vit; //生命力
  4. 4 private int atk; //攻击力
  5. 5 private int def; //防御力
  6. 6
  7. 7 public int getVit() {
  8. 8 return vit;
  9. 9 }
  10. 10 public void setVit(int vit) {
  11. 11 this.vit = vit;
  12. 12 }
  13. 13 public int getAtk() {
  14. 14 return atk;
  15. 15 }
  16. 16 public void setAtk(int atk) {
  17. 17 this.atk = atk;
  18. 18 }
  19. 19 public int getDef() {
  20. 20 return def;
  21. 21 }
  22. 22 public void setDef(int def) {
  23. 23 this.def = def;
  24. 24 }
  25. 25
  26. 26 //状态显示
  27. 27 public void stateDisplay() {
  28. 28 System.out.println("角色当前状态:");
  29. 29 System.out.println("体力:" + this.vit);
  30. 30 System.out.println("攻击力:" + this.atk);
  31. 31 System.out.println("防御力: " + this.def);
  32. 32 System.out.println("-----------------");
  33. 33 }
  34. 34
  35. 35 //获得初始状态
  36. 36 public void getInitState() {
  37. 37 this.vit = 100;
  38. 38 this.atk = 100;
  39. 39 this.def = 100;
  40. 40 }
  41. 41
  42. 42 //战斗后
  43. 43 public void fight() {
  44. 44 this.vit = 0;
  45. 45 this.atk = 0;
  46. 46 this.def = 0;
  47. 47 }
  48. 48
  49. 49 //保存角色状态
  50. 50 public RoleStateMemento saveState() {
  51. 51 return (new RoleStateMemento(vit, atk, def));
  52. 52 }
  53. 53
  54. 54 //恢复角色状态
  55. 55 public void recoveryState(RoleStateMemento memento) {
  56. 56 this.vit = memento.getVit();
  57. 57 this.atk = memento.getAtk();
  58. 58 this.def = memento.getDef();
  59. 59 }
  60. 60
  61. 61 }

  备忘录类,用于存储角色状态。

  1. 1 public class RoleStateMemento {
  2. 2
  3. 3 private int vit; //生命力
  4. 4 private int atk; //攻击力
  5. 5 private int def; //防御力
  6. 6
  7. 7 public RoleStateMemento(int vit, int atk, int def) {
  8. 8 this.vit = vit;
  9. 9 this.atk = atk;
  10. 10 this.def = def;
  11. 11 }
  12. 12
  13. 13 public int getVit() {
  14. 14 return vit;
  15. 15 }
  16. 16
  17. 17 public void setVit(int vit) {
  18. 18 this.vit = vit;
  19. 19 }
  20. 20
  21. 21 public int getAtk() {
  22. 22 return atk;
  23. 23 }
  24. 24
  25. 25 public void setAtk(int atk) {
  26. 26 this.atk = atk;
  27. 27 }
  28. 28
  29. 29 public int getDef() {
  30. 30 return def;
  31. 31 }
  32. 32
  33. 33 public void setDef(int def) {
  34. 34 this.def = def;
  35. 35 }
  36. 36
  37. 37 }

  备忘录管理者。

  1. 1 public class RoleStateCaretaker {
  2. 2
  3. 3 private RoleStateMemento memento;
  4. 4
  5. 5 public RoleStateMemento getMemento() {
  6. 6 return memento;
  7. 7 }
  8. 8
  9. 9 public void setMemento(RoleStateMemento memento) {
  10. 10 this.memento = memento;
  11. 11 }
  12. 12
  13. 13 }

  下面编写一个简单的程序测试一下,编写逻辑大致为打boss前存档,打boss失败了,读档。

  1. 1 public class Client {
  2. 2
  3. 3 public static void main(String[] args) {
  4. 4 //打boss前
  5. 5 GameRole gameRole = new GameRole();
  6. 6 gameRole.getInitState();
  7. 7 gameRole.stateDisplay();
  8. 8
  9. 9 //保存进度
  10. 10 RoleStateCaretaker caretaker = new RoleStateCaretaker();
  11. 11 caretaker.setMemento(gameRole.saveState());
  12. 12
  13. 13 //打boss失败
  14. 14 gameRole.fight();
  15. 15 gameRole.stateDisplay();
  16. 16
  17. 17 //恢复状态
  18. 18 gameRole.recoveryState(caretaker.getMemento());
  19. 19 gameRole.stateDisplay();
  20. 20 }
  21. 21
  22. 22 }

  运行结果如下:

  

 

  源码地址:https://gitee.com/adamjiangwh/GoF  

版权声明:本文为adamjwh原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/adamjwh/p/11018268.html