设计模式(18)——备忘录模式

来源:互联网 发布:c语言开发实战宝典 编辑:程序博客网 时间:2024/05/17 03:33

备忘录模式

一、模式定义

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

备忘录模式又叫做快照模式(Snapshot Pattern)或Token模式,属于行为模式。

二、 结构

组成部分:

  1. 发起人(Originator):负责创建一个备忘录Memento,用以记录当前时刻自身的内部状态,并可使用备忘录恢复内部状态。发起人可以根据需要觉得备忘录存储自己的哪些内部状态。
  2. 备忘录(Memento):负责存储发起人对象的内部状态,并可以防止发起人以外的其他对象访问备忘录。备忘录有两个接口:管理者只能看到备忘录的窄接口,他只能将备忘录传递给其他对象。发起人却可以看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。
  3. 管理者(Caretaker):负责备忘录,不能对备忘录的内容进行访问或者操作。

三、例子---游戏进度保存

实例:以保存游戏进度为例,在游戏角色大战Boss前将该角色的状态存储,与Boss作战后角色的各项能力会下降,如果没有通关,则可利用备忘录进行恢复到战前状态。

Memento

[java] view plaincopyprint?
  1. public class Memento {  
  2.     private int vitality; //生命力  
  3.     private int aggressivity; //攻击力  
  4.     private int defencivity; //防御力  
  5.   
  6.     public int getVitality() {  
  7.         return vitality;  
  8.     }  
  9.   
  10.     public void setVitality(int vitality) {  
  11.         this.vitality = vitality;  
  12.     }  
  13.   
  14.     public int getAggressivity() {  
  15.         return aggressivity;  
  16.     }  
  17.   
  18.     public void setAggressivity(int aggressivity) {  
  19.         this.aggressivity = aggressivity;  
  20.     }  
  21.   
  22.     public int getDefencivity() {  
  23.         return defencivity;  
  24.     }  
  25.   
  26.     public void setDefencivity(int defencivity) {  
  27.         this.defencivity = defencivity;  
  28.     }  
  29. }  

发起人可以看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。

[java] view plaincopyprint?
  1. public class PlayerOriginator {  
  2.     private int vitality; //生命力  
  3.     private int aggressivity; //攻击力  
  4.     private int defencivity; //防御力  
  5.   
  6.     public PlayerOriginator(int vitality, int aggressivity, int defencivity) {  
  7.         this.vitality = vitality;  
  8.         this.aggressivity = aggressivity;  
  9.         this.defencivity = defencivity;  
  10.     }  
  11.   
  12.     public int getVitality() {  
  13.         return vitality;  
  14.     }  
  15.   
  16.     public void setVitality(int vitality) {  
  17.         this.vitality = vitality;  
  18.     }  
  19.   
  20.     public int getAggressivity() {  
  21.         return aggressivity;  
  22.     }  
  23.   
  24.     public void setAggressivity(int aggressivity) {  
  25.         this.aggressivity = aggressivity;  
  26.     }  
  27.   
  28.     public int getDefencivity() {  
  29.         return defencivity;  
  30.     }  
  31.   
  32.     public void setDefencivity(int defencivity) {  
  33.         this.defencivity = defencivity;  
  34.     }  
  35.   
  36.     public Memento createMemento() {  
  37.         Memento memento = new Memento();  
  38.         memento.setVitality(this.vitality);  
  39.         memento.setAggressivity(this.aggressivity);  
  40.         memento.setDefencivity(this.defencivity);  
  41.         return memento;  
  42.     }  
  43.   
  44.     public void setMemento(Memento memento) {  
  45.         this.vitality = memento.getVitality();  
  46.         this.aggressivity = memento.getAggressivity();  
  47.         this.defencivity = memento.getDefencivity();  
  48.     }  
  49.   
  50.     public void showState() {  
  51.         System.out.print("vitality:" + this.vitality);  
  52.         System.out.print("; aggressivity:" + this.aggressivity);  
  53.         System.out.println("; defencivity:" + this.defencivity);  
  54.     }  
  55. }  

管理者  只能看到备忘录的窄接口,他只能将备忘录传递给其他对象

[java] view plaincopyprint?
  1. public class Caretaker {  
  2.     private Memento memento;  
  3.   
  4.     public Memento getMemento() {  
  5.         return memento;  
  6.     }  
  7.   
  8.     public void setMemento( Memento memento) {  
  9.         this.memento = memento;  
  10.     }  
  11. }  

场景类

[java] view plaincopyprint?
  1. public class Client {  
  2.     public static void main(final String[] args) {  
  3.         PlayerOriginator player = new PlayerOriginator(100100100);  
  4.         System.out.print("player's original attributes are ");  
  5.         player.showState();  
  6.   
  7.         Caretaker taker = new Caretaker();  
  8.         taker.setMemento(player.createMemento());  
  9.   
  10.         player.setVitality(70);  
  11.         player.setAggressivity(60);  
  12.         player.setDefencivity(20);  
  13.   
  14.         System.out.print("after player fight with Boss, player's attributes are ");  
  15.         player.showState();  
  16.   
  17.         //reset player's attribute  
  18.         player.setMemento(taker.getMemento());  
  19.         System.out.print("after resetting, player's attributes are ");  
  20.         player.showState();  
  21.   
  22.     }  
  23.   
  24. }  

运行结果

player's original attributes are vitality:100; aggressivity:100; defencivity:100
after player fight with Boss, player's attributes are vitality:70; aggressivity:60; defencivity:20
after resetting, player's attributes are vitality:100; aggressivity:100; defencivity:100

四、 适用场景

  • 在程序运行过程中,某些对象的状态处在转换过程中,可能由于某种原因需要保存此时对象的状态,以便程序运行到某个特定阶段,需要恢复到对象之前处于某个点时的状态。
  • 如果使用一些公有接口让其它对象来得到对象的状态,便会暴露对象的实现细节。
  • 如果有需要提供回滚操作的需求,使用备忘录模式非常适合,比如jdbc的事务操作,文本编辑器的Ctrl+Z恢复等。

五、备忘录模式的优点:

·当发起人角色中的状态改变时,有可能这是个错误的改变,我们使用备忘录模式就可以把这个错误的改变还原。

·备份的状态是保存在发起人角色之外的,这样,发起人角色就不需要对各个备份的状态进行管理。

六、备忘录模式的缺点:

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

七、多状态多备份备忘录

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

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

运行结果

===初始化状态===
state1=中国state2=强盛state3=繁荣
===修改后状态===
state1=软件state2=架构state3=优秀
===恢复后状态===
state1=中国state2=强盛state3=繁荣

参考:

http://www.cnblogs.com/itTeacher/archive/2012/12/12/2814814.html

http://www.blogjava.net/flustar/archive/2007/12/08/memento.html

http://blog.csdn.net/zhengzhb/article/details/7697549

0 0