备忘录模式

定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态

类型:行为类

类图:

备忘录模式

我们在编程的时候,经常需要保存对象的中间状态,当需要的时候,可以恢复到这个状态。比如,我们使用Eclipse进行编程时,假如编写失误(例如不小心误删除了几行代码),我们希望返回删除前的状态,便可以使用Ctrl+Z来进行返回。这时我们便可以使用备忘录模式来实现。

备忘录模式的结构

  • 发起人:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。
  • 备忘录:负责存储发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。
  • 管理角色:对备忘录进行管理,保存和提供备忘录。

通用代码实现

  1. classOriginator{
  2. privateStringstate="";
  3. publicStringgetState(){
  4. returnstate;
  5. }
  6. publicvoidsetState(Stringstate){
  7. this.state=state;
  8. }
  9. publicMementocreateMemento(){
  10. returnnewMemento(this.state);
  11. }
  12. publicvoidrestoreMemento(Mementomemento){
  13. this.setState(memento.getState());
  14. }
  15. }
  16. classMemento{
  17. privateStringstate="";
  18. publicMemento(Stringstate){
  19. this.state=state;
  20. }
  21. publicStringgetState(){
  22. returnstate;
  23. }
  24. publicvoidsetState(Stringstate){
  25. this.state=state;
  26. }
  27. }
  28. classCaretaker{
  29. privateMementomemento;
  30. publicMementogetMemento(){
  31. returnmemento;
  32. }
  33. publicvoidsetMemento(Mementomemento){
  34. this.memento=memento;
  35. }
  36. }
  37. publicclassClient{
  38. publicstaticvoidmain(String[]args){
  39. Originatororiginator=newOriginator();
  40. originator.setState("状态1");
  41. System.out.println("初始状态:"+originator.getState());
  42. Caretakercaretaker=newCaretaker();
  43. caretaker.setMemento(originator.createMemento());
  44. originator.setState("状态2");
  45. System.out.println("改变后状态:"+originator.getState());
  46. originator.restoreMemento(caretaker.getMemento());
  47. System.out.println("恢复后状态:"+originator.getState());
  48. }
  49. }

代码演示了一个单状态单备份的例子,逻辑非常简单:Originator类中的state变量需要备份,以便在需要的时候恢复;Memento类中,也有一个state变量,用来存储Originator类中state变量的临时状态;而Caretaker类就是用来管理备忘录类的,用来向备忘录对象中写入状态或者取回状态。

多状态多备份备忘录

通用代码演示的例子中,Originator类只有一个state变量需要备份,而通常情况下,发起人角色通常是一个javaBean,对象中需要备份的变量不止一个,需要备份的状态也不止一个,这就是多状态多备份备忘录。实现备忘录的方法很多,备忘录模式有很多变形和处理方式,像通用代码那样的方式一般不会用到,多数情况下的备忘录模式,是多状态多备份的。其实实现多状态多备份也很简单,最常用的方法是,我们在Memento中增加一个Map容器来存储所有的状态,在Caretaker类中同样使用一个Map容器才存储所有的备份。下面我们给出一个多状态多备份的例子:

  1. classOriginator{
  2. privateStringstate1="";
  3. privateStringstate2="";
  4. privateStringstate3="";
  5. publicStringgetState1(){
  6. returnstate1;
  7. }
  8. publicvoidsetState1(Stringstate1){
  9. this.state1=state1;
  10. }
  11. publicStringgetState2(){
  12. returnstate2;
  13. }
  14. publicvoidsetState2(Stringstate2){
  15. this.state2=state2;
  16. }
  17. publicStringgetState3(){
  18. returnstate3;
  19. }
  20. publicvoidsetState3(Stringstate3){
  21. this.state3=state3;
  22. }
  23. publicMementocreateMemento(){
  24. returnnewMemento(BeanUtils.backupProp(this));
  25. }
  26. publicvoidrestoreMemento(Mementomemento){
  27. BeanUtils.restoreProp(this,memento.getStateMap());
  28. }
  29. publicStringtoString(){
  30. return"state1="+state1+"state2="+state2+"state3="+state3;
  31. }
  32. }
  33. classMemento{
  34. privateMap<String,Object>stateMap;
  35. publicMemento(Map<String,Object>map){
  36. this.stateMap=map;
  37. }
  38. publicMap<String,Object>getStateMap(){
  39. returnstateMap;
  40. }
  41. publicvoidsetStateMap(Map<String,Object>stateMap){
  42. this.stateMap=stateMap;
  43. }
  44. }
  45. classBeanUtils{
  46. publicstaticMap<String,Object>backupProp(Objectbean){
  47. Map<String,Object>result=newHashMap<String,Object>();
  48. try{
  49. BeanInfobeanInfo=Introspector.getBeanInfo(bean.getClass());
  50. PropertyDescriptor[]descriptors=beanInfo.getPropertyDescriptors();
  51. for(PropertyDescriptordes:descriptors){
  52. StringfieldName=des.getName();
  53. Methodgetter=des.getReadMethod();
  54. ObjectfieldValue=getter.invoke(bean,newObject[]{});
  55. if(!fieldName.equalsIgnoreCase("class")){
  56. result.put(fieldName,fieldValue);
  57. }
  58. }
  59. }catch(Exceptione){
  60. e.printStackTrace();
  61. }
  62. returnresult;
  63. }
  64. publicstaticvoidrestoreProp(Objectbean,Map<String,Object>propMap){
  65. try{
  66. BeanInfobeanInfo=Introspector.getBeanInfo(bean.getClass());
  67. PropertyDescriptor[]descriptors=beanInfo.getPropertyDescriptors();
  68. for(PropertyDescriptordes:descriptors){
  69. StringfieldName=des.getName();
  70. if(propMap.containsKey(fieldName)){
  71. Methodsetter=des.getWriteMethod();
  72. setter.invoke(bean,newObject[]{propMap.get(fieldName)});
  73. }
  74. }
  75. }catch(Exceptione){
  76. e.printStackTrace();
  77. }
  78. }
  79. }
  80. classCaretaker{
  81. privateMap<String,Memento>memMap=newHashMap<String,Memento>();
  82. publicMementogetMemento(Stringindex){
  83. returnmemMap.get(index);
  84. }
  85. publicvoidsetMemento(Stringindex,Mementomemento){
  86. this.memMap.put(index,memento);
  87. }
  88. }
  89. classClient{
  90. publicstaticvoidmain(String[]args){
  91. Originatorori=newOriginator();
  92. Caretakercaretaker=newCaretaker();
  93. ori.setState1("中国");
  94. ori.setState2("强盛");
  95. ori.setState3("繁荣");
  96. System.out.println("===初始化状态===\n"+ori);
  97. caretaker.setMemento("001",ori.createMemento());
  98. ori.setState1("软件");
  99. ori.setState2("架构");
  100. ori.setState3("优秀");
  101. System.out.println("===修改后状态===\n"+ori);
  102. ori.restoreMemento(caretaker.getMemento("001"));
  103. System.out.println("===恢复后状态===\n"+ori);
  104. }
  105. }


备忘录模式的优缺点和适用场景

备忘录模式的优点有:

  • 当发起人角色中的状态改变时,有可能这是个错误的改变,我们使用备忘录模式就可以把这个错误的改变还原。
  • 备份的状态是保存在发起人角色之外的,这样,发起人角色就不需要对各个备份的状态进行管理。

备忘录模式的缺点:

  • 在实际应用中,备忘录模式都是多状态和多备份的,发起人角色的状态需要存储到备忘录对象中,对资源的消耗是比较严重的。

如果有需要提供回滚操作的需求,使用备忘录模式非常适合,比如jdbc的事务操作,文本编辑器的Ctrl+Z恢复等。

定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态

类型:行为类

类图:

备忘录模式

我们在编程的时候,经常需要保存对象的中间状态,当需要的时候,可以恢复到这个状态。比如,我们使用Eclipse进行编程时,假如编写失误(例如不小心误删除了几行代码),我们希望返回删除前的状态,便可以使用Ctrl+Z来进行返回。这时我们便可以使用备忘录模式来实现。

备忘录模式的结构

  • 发起人:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。
  • 备忘录:负责存储发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。
  • 管理角色:对备忘录进行管理,保存和提供备忘录。

通用代码实现

  1. classOriginator{
  2. privateStringstate="";
  3. publicStringgetState(){
  4. returnstate;
  5. }
  6. publicvoidsetState(Stringstate){
  7. this.state=state;
  8. }
  9. publicMementocreateMemento(){
  10. returnnewMemento(this.state);
  11. }
  12. publicvoidrestoreMemento(Mementomemento){
  13. this.setState(memento.getState());
  14. }
  15. }
  16. classMemento{
  17. privateStringstate="";
  18. publicMemento(Stringstate){
  19. this.state=state;
  20. }
  21. publicStringgetState(){
  22. returnstate;
  23. }
  24. publicvoidsetState(Stringstate){
  25. this.state=state;
  26. }
  27. }
  28. classCaretaker{
  29. privateMementomemento;
  30. publicMementogetMemento(){
  31. returnmemento;
  32. }
  33. publicvoidsetMemento(Mementomemento){
  34. this.memento=memento;
  35. }
  36. }
  37. publicclassClient{
  38. publicstaticvoidmain(String[]args){
  39. Originatororiginator=newOriginator();
  40. originator.setState("状态1");
  41. System.out.println("初始状态:"+originator.getState());
  42. Caretakercaretaker=newCaretaker();
  43. caretaker.setMemento(originator.createMemento());
  44. originator.setState("状态2");
  45. System.out.println("改变后状态:"+originator.getState());
  46. originator.restoreMemento(caretaker.getMemento());
  47. System.out.println("恢复后状态:"+originator.getState());
  48. }
  49. }

代码演示了一个单状态单备份的例子,逻辑非常简单:Originator类中的state变量需要备份,以便在需要的时候恢复;Memento类中,也有一个state变量,用来存储Originator类中state变量的临时状态;而Caretaker类就是用来管理备忘录类的,用来向备忘录对象中写入状态或者取回状态。

多状态多备份备忘录

通用代码演示的例子中,Originator类只有一个state变量需要备份,而通常情况下,发起人角色通常是一个javaBean,对象中需要备份的变量不止一个,需要备份的状态也不止一个,这就是多状态多备份备忘录。实现备忘录的方法很多,备忘录模式有很多变形和处理方式,像通用代码那样的方式一般不会用到,多数情况下的备忘录模式,是多状态多备份的。其实实现多状态多备份也很简单,最常用的方法是,我们在Memento中增加一个Map容器来存储所有的状态,在Caretaker类中同样使用一个Map容器才存储所有的备份。下面我们给出一个多状态多备份的例子:

  1. classOriginator{
  2. privateStringstate1="";
  3. privateStringstate2="";
  4. privateStringstate3="";
  5. publicStringgetState1(){
  6. returnstate1;
  7. }
  8. publicvoidsetState1(Stringstate1){
  9. this.state1=state1;
  10. }
  11. publicStringgetState2(){
  12. returnstate2;
  13. }
  14. publicvoidsetState2(Stringstate2){
  15. this.state2=state2;
  16. }
  17. publicStringgetState3(){
  18. returnstate3;
  19. }
  20. publicvoidsetState3(Stringstate3){
  21. this.state3=state3;
  22. }
  23. publicMementocreateMemento(){
  24. returnnewMemento(BeanUtils.backupProp(this));
  25. }
  26. publicvoidrestoreMemento(Mementomemento){
  27. BeanUtils.restoreProp(this,memento.getStateMap());
  28. }
  29. publicStringtoString(){
  30. return"state1="+state1+"state2="+state2+"state3="+state3;
  31. }
  32. }
  33. classMemento{
  34. privateMap<String,Object>stateMap;
  35. publicMemento(Map<String,Object>map){
  36. this.stateMap=map;
  37. }
  38. publicMap<String,Object>getStateMap(){
  39. returnstateMap;
  40. }
  41. publicvoidsetStateMap(Map<String,Object>stateMap){
  42. this.stateMap=stateMap;
  43. }
  44. }
  45. classBeanUtils{
  46. publicstaticMap<String,Object>backupProp(Objectbean){
  47. Map<String,Object>result=newHashMap<String,Object>();
  48. try{
  49. BeanInfobeanInfo=Introspector.getBeanInfo(bean.getClass());
  50. PropertyDescriptor[]descriptors=beanInfo.getPropertyDescriptors();
  51. for(PropertyDescriptordes:descriptors){
  52. StringfieldName=des.getName();
  53. Methodgetter=des.getReadMethod();
  54. ObjectfieldValue=getter.invoke(bean,newObject[]{});
  55. if(!fieldName.equalsIgnoreCase("class")){
  56. result.put(fieldName,fieldValue);
  57. }
  58. }
  59. }catch(Exceptione){
  60. e.printStackTrace();
  61. }
  62. returnresult;
  63. }
  64. publicstaticvoidrestoreProp(Objectbean,Map<String,Object>propMap){
  65. try{
  66. BeanInfobeanInfo=Introspector.getBeanInfo(bean.getClass());
  67. PropertyDescriptor[]descriptors=beanInfo.getPropertyDescriptors();
  68. for(PropertyDescriptordes:descriptors){
  69. StringfieldName=des.getName();
  70. if(propMap.containsKey(fieldName)){
  71. Methodsetter=des.getWriteMethod();
  72. setter.invoke(bean,newObject[]{propMap.get(fieldName)});
  73. }
  74. }
  75. }catch(Exceptione){
  76. e.printStackTrace();
  77. }
  78. }
  79. }
  80. classCaretaker{
  81. privateMap<String,Memento>memMap=newHashMap<String,Memento>();
  82. publicMementogetMemento(Stringindex){
  83. returnmemMap.get(index);
  84. }
  85. publicvoidsetMemento(Stringindex,Mementomemento){
  86. this.memMap.put(index,memento);
  87. }
  88. }
  89. classClient{
  90. publicstaticvoidmain(String[]args){
  91. Originatorori=newOriginator();
  92. Caretakercaretaker=newCaretaker();
  93. ori.setState1("中国");
  94. ori.setState2("强盛");
  95. ori.setState3("繁荣");
  96. System.out.println("===初始化状态===\n"+ori);
  97. caretaker.setMemento("001",ori.createMemento());
  98. ori.setState1("软件");
  99. ori.setState2("架构");
  100. ori.setState3("优秀");
  101. System.out.println("===修改后状态===\n"+ori);
  102. ori.restoreMemento(caretaker.getMemento("001"));
  103. System.out.println("===恢复后状态===\n"+ori);
  104. }
  105. }


备忘录模式的优缺点和适用场景

备忘录模式的优点有:

  • 当发起人角色中的状态改变时,有可能这是个错误的改变,我们使用备忘录模式就可以把这个错误的改变还原。
  • 备份的状态是保存在发起人角色之外的,这样,发起人角色就不需要对各个备份的状态进行管理。

备忘录模式的缺点:

  • 在实际应用中,备忘录模式都是多状态和多备份的,发起人角色的状态需要存储到备忘录对象中,对资源的消耗是比较严重的。

如果有需要提供回滚操作的需求,使用备忘录模式非常适合,比如jdbc的事务操作,文本编辑器的Ctrl+Z恢复等。

你可能感兴趣的:(备忘录模式)