Java设计模式之备忘录模式

来源:互联网 发布:港澳台电视直播软件tv 编辑:程序博客网 时间:2024/05/29 10:16
本文继续23种设计模式系列之备忘录模式。

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

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

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

应用
[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. class Originator {    
  2.     private String state = "";    
  3.         
  4.     public String getState() {    
  5.         return state;    
  6.     }    
  7.     public void setState(String state) {    
  8.         this.state = state;    
  9.     }    
  10.     public Memento createMemento(){    
  11.         return new Memento(this.state);    
  12.     }    
  13.     public void restoreMemento(Memento memento){    
  14.         this.setState(memento.getState());    
  15.     }    
  16. }    
  17.     
  18. class Memento {    
  19.     private String state = "";    
  20.     public Memento(String state){    
  21.         this.state = state;    
  22.     }    
  23.     public String getState() {    
  24.         return state;    
  25.     }    
  26.     public void setState(String state) {    
  27.         this.state = state;    
  28.     }    
  29. }    
  30. class Caretaker {    
  31.     private Memento memento;    
  32.     public Memento getMemento(){    
  33.         return memento;    
  34.     }    
  35.     public void setMemento(Memento memento){    
  36.         this.memento = memento;    
  37.     }    
  38. }    
  39. public class Client {    
  40.     public static void main(String[] args){    
  41.         Originator originator = new Originator();    
  42.         originator.setState("状态1");    
  43.         System.out.println("初始状态:"+originator.getState());    
  44.         Caretaker caretaker = new Caretaker();    
  45.         caretaker.setMemento(originator.createMemento());    
  46.         originator.setState("状态2");    
  47.         System.out.println("改变后状态:"+originator.getState());    
  48.         originator.restoreMemento(caretaker.getMemento());    
  49.         System.out.println("恢复后状态:"+originator.getState());    
  50.     }    
  51. }    

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

 

多状态多备份备忘录

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

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. class Originator {    
  2.     private String state1 = "";    
  3.     private String state2 = "";    
  4.     private String state3 = "";    
  5.     
  6.     public String getState1() {    
  7.         return state1;    
  8.     }    
  9.     public void setState1(String state1) {    
  10.         this.state1 = state1;    
  11.     }    
  12.     public String getState2() {    
  13.         return state2;    
  14.     }    
  15.     public void setState2(String state2) {    
  16.         this.state2 = state2;    
  17.     }    
  18.     public String getState3() {    
  19.         return state3;    
  20.     }    
  21.     public void setState3(String state3) {    
  22.         this.state3 = state3;    
  23.     }    
  24.     public Memento createMemento(){    
  25.         return new Memento(BeanUtils.backupProp(this));    
  26.     }    
  27.         
  28.     public void restoreMemento(Memento memento){    
  29.         BeanUtils.restoreProp(this, memento.getStateMap());    
  30.     }    
  31.     public String toString(){    
  32.         return "state1="+state1+"state2="+state2+"state3="+state3;    
  33.     }    
  34. }    
  35. class Memento {    
  36.     private Map<String, Object> stateMap;    
  37.         
  38.     public Memento(Map<String, Object> map){    
  39.         this.stateMap = map;    
  40.     }    
  41.     
  42.     public Map<String, Object> getStateMap() {    
  43.         return stateMap;    
  44.     }    
  45.     
  46.     public void setStateMap(Map<String, Object> stateMap) {    
  47.         this.stateMap = stateMap;    
  48.     }    
  49. }    
  50. class BeanUtils {    
  51.     public static Map<String, Object> backupProp(Object bean){    
  52.         Map<String, Object> result = new HashMap<String, Object>();    
  53.         try{    
  54.             BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());    
  55.             PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();    
  56.             for(PropertyDescriptor des: descriptors){    
  57.                 String fieldName = des.getName();    
  58.                 Method getter = des.getReadMethod();    
  59.                 Object fieldValue = getter.invoke(bean, new Object[]{});    
  60.                 if(!fieldName.equalsIgnoreCase("class")){    
  61.                     result.put(fieldName, fieldValue);    
  62.                 }    
  63.             }    
  64.                 
  65.         }catch(Exception e){    
  66.             e.printStackTrace();    
  67.         }    
  68.         return result;    
  69.     }    
  70.         
  71.     public static void restoreProp(Object bean, Map<String, Object> propMap){    
  72.         try {    
  73.             BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());    
  74.             PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();    
  75.             for(PropertyDescriptor des: descriptors){    
  76.                 String fieldName = des.getName();    
  77.                 if(propMap.containsKey(fieldName)){    
  78.                     Method setter = des.getWriteMethod();    
  79.                     setter.invoke(bean, new Object[]{propMap.get(fieldName)});    
  80.                 }    
  81.             }    
  82.         } catch (Exception e) {    
  83.             e.printStackTrace();    
  84.         }    
  85.     }    
  86. }    
  87. class Caretaker {    
  88.     private Map<String, Memento> memMap = new HashMap<String, Memento>();    
  89.     public Memento getMemento(String index){    
  90.         return memMap.get(index);    
  91.     }    
  92.         
  93.     public void setMemento(String index, Memento memento){    
  94.         this.memMap.put(index, memento);    
  95.     }    
  96. }    
  97. class Client {    
  98.     public static void main(String[] args){    
  99.         Originator ori = new Originator();    
  100.         Caretaker caretaker = new Caretaker();    
  101.         ori.setState1("中国");    
  102.         ori.setState2("强盛");    
  103.         ori.setState3("繁荣");    
  104.         System.out.println("===初始化状态===\n"+ori);    
  105.             
  106.         caretaker.setMemento("001",ori.createMemento());    
  107.         ori.setState1("软件");    
  108.         ori.setState2("架构");    
  109.         ori.setState3("优秀");    
  110.         System.out.println("===修改后状态===\n"+ori);    
  111.             
  112.         ori.restoreMemento(caretaker.getMemento("001"));    
  113.         System.out.println("===恢复后状态===\n"+ori);    
  114.     }    
  115. } <span style="font-family: Consolas, 'Courier New', Courier, mono, serif; background-color: inherit;"> </span>  


总结
优点
1、当发起人角色中的状态改变时,有可能这是个错误的改变,我们使用备忘录模式就可以把这个错误的改变还原。
2、备份的状态是保存在发起人角色之外的,这样,发起人角色就不需要对各个备份的状态进行管理。
缺点
1、在实际应用中,备忘录模式都是多状态和多备份的,发起人角色的状态需要存储到备忘录对象中,对资源的消耗是比较严重的。
2、如果有需要提供回滚操作的需求,使用备忘录模式非常适合,比如jdbc的事务操作,文本编辑器的Ctrl+Z恢复等。

更多设计模式:23种设计模式系列

作者:jason0539

博客:http://blog.csdn.net/jason0539(转载请说明出处)

0 0
原创粉丝点击