设计模式之禅之设计模式-备忘录模式
《一:备忘录模式的定义
--->备忘录模式(Memento Pattern)提供了一种弥补真实世界缺陷的方法,让“后悔药”在程序的世界中真实可行
--->在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
--->通俗地说,备忘录模式就是一个对象的备份模式,提供了一种程序数据的备份方法
二:备忘录模式的角色
● Originator发起人角色
记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。
● Memento备忘录角色
负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。
● Caretaker备忘录管理员角色
对备忘录进行管理、保存和提供备忘录。
三:备忘录模式的应用
【1】备忘录模式使用场景
● 需要保存和恢复数据的相关状态场景。
● 提供一个可回滚(rollback)的操作;比如Word中的CTRL+Z组合键,IE浏览器中的后退按钮,文件管理器上的backspace键等。
● 需要监控的副本场景中。
● 数据库连接的事务管理就是用的备忘录模式,想想看,如果你要实现一个JDBC驱动,你怎么来实现事务?还不是用备忘录模式嘛!
【2】备忘录模式的注意事项
● 备忘录的生命期
备忘录创建出来就要在“最近”的代码中使用,要主动管理它的生命周期,建立就要使用,不使用就要立刻删除其引用,等待垃圾回收器对它的回收处理。
● 备忘录的性能
不要在频繁建立备份的场景中使用备忘录模式(比如一个for循环中),原因有二:一是控制不了备忘录建立的对象数量;二是大对象的建立是要消耗资源的,系统的性能需要考虑。因此,如果出现这样的代码,设计师就应该好好想想怎么修改架构了。
【3】备忘录模式的最佳实践
备忘录模式是我们设计上“月光宝盒”,可以让我们回到需要的年代;是程序数据的“后悔药”,吃了它就可以返回上一个状态;是设计人员的定心丸,确保即使在最坏的情况下也能获得最近的对象状态。如果大家看懂了的话,请各位在设计的时候就不要使用数据库的临时表作为缓存备份数据了,虽然是一个简单的办法,但是它加大了数据库操作的频繁度,把压力下放到数据库了,最好的解决办法就是使用备忘录模式。
四:备忘录模式的案例
【1】简单备忘录模式的标准
《一》发起者
1 package com.yeepay.sxf.template19; 2 /** 3 * 【简单标准的备忘录模式】的发起者 4 * @author sxf 5 * 6 */ 7 public class Originator { 8 //内部状态 9 private String state=""; 10 11 public String getState() { 12 return state; 13 } 14 15 public void setState(String state) { 16 this.state = state; 17 } 18 19 //创建一个备忘录 20 public Memento createMemento(){ 21 return new Memento(this.state); 22 } 23 //恢复一个备忘录 24 public void restoreMemento(Memento memento){ 25 this.setState(memento.getState()); 26 } 27 28 }
《二》备忘录
1 package com.yeepay.sxf.template19; 2 /** 3 * 备忘录角色 4 * @author sxf 5 * 6 */ 7 public class Memento { 8 //发起人的内部状态 9 private String state=""; 10 11 public Memento(String _state){ 12 this.state=_state; 13 } 14 15 public String getState() { 16 return state; 17 } 18 19 public void setState(String state) { 20 this.state = state; 21 } 22 23 }
《三》备忘录管理
1 package com.yeepay.sxf.template19; 2 /** 3 * 备忘录的管理员角色 4 * @author sxf 5 * 6 */ 7 public class Caretaker { 8 9 private Memento memento; 10 11 public Memento getMemento() { 12 return memento; 13 } 14 15 public void setMemento(Memento memento) { 16 this.memento = memento; 17 } 18 19 20 }
【2】基于克隆的备忘模式
《一》发起者
1 package com.yeepay.sxf.template19; 2 /** 3 * 【结合原型模式的备忘录模式】 4 * 使用Clone方式的备忘录模式,可以使用在比较简单的场景或者比较单一的场景 5 * 中,尽量不要与其他的对象产生严重的耦合关系。 6 * @author sxf 7 * 8 */ 9 public class Originatord implements Cloneable { 10 //内部备忘录 11 private Originatord originatord; 12 13 //初始化状态 14 private String state=""; 15 16 //创建一个备忘录 17 public void createMemento(){ 18 try { 19 Originatord dOriginatord=this.clone(); 20 this.originatord=dOriginatord; 21 } catch (CloneNotSupportedException e) { 22 // TODO Auto-generated catch block 23 e.printStackTrace(); 24 } 25 } 26 //恢复备忘 27 public void resetMemento(){ 28 this.setState(this.originatord.getState()); 29 } 30 public Originatord getOriginatord() { 31 return originatord; 32 } 33 34 public void setOriginatord(Originatord originatord) { 35 this.originatord = originatord; 36 } 37 38 public String getState() { 39 return state; 40 } 41 42 public void setState(String state) { 43 this.state = state; 44 } 45 46 /** 47 * 使用克隆进行备忘 48 */ 49 @Override 50 protected Originatord clone() throws CloneNotSupportedException { 51 return (Originatord) super.clone(); 52 53 } 54 55 56 }
【3】多状态的备忘模式
《一》多状态发起者
1 package com.yeepay.sxf.template19; 2 3 import java.util.HashMap; 4 5 /** 6 *【 多状态的备忘录模式】 7 * @author sxf 8 * 9 */ 10 public class OriginatorDuo { 11 //内部状态 12 private String state1=""; 13 private String state2=""; 14 private String state3=""; 15 16 //创建一个备忘录 17 public MementoDuo createMemenTo(){ 18 return new MementoDuo(BeanUtils.backupProp(this)); 19 } 20 21 //恢复备忘 22 public void resetMemento(HashMap<String, Object> propMap){ 23 BeanUtils.restoreProp(this, propMap); 24 } 25 public String getState1() { 26 return state1; 27 } 28 public void setState1(String state1) { 29 this.state1 = state1; 30 } 31 public String getState2() { 32 return state2; 33 } 34 public void setState2(String state2) { 35 this.state2 = state2; 36 } 37 public String getState3() { 38 return state3; 39 } 40 public void setState3(String state3) { 41 this.state3 = state3; 42 } 43 44 }
《二》多状态备忘录
1 package com.yeepay.sxf.template19; 2 3 import java.util.HashMap; 4 5 /** 6 * 多状态的备忘录 7 * @author sxf 8 * 9 */ 10 public class MementoDuo { 11 //接受HashMap作为状态 12 private HashMap<String, Object> stateMap; 13 //接受一个对象,建立一个备份 14 public MementoDuo(HashMap<String, Object> stateMap){ 15 this.stateMap=stateMap; 16 } 17 public HashMap<String, Object> getStateMap() { 18 return stateMap; 19 } 20 public void setStateMap(HashMap<String, Object> stateMap) { 21 this.stateMap = stateMap; 22 } 23 24 25 }
《三》多状态备忘录的创建和恢复的工具类
1 package com.yeepay.sxf.template19; 2 3 import java.beans.BeanInfo; 4 import java.beans.Introspector; 5 import java.beans.PropertyDescriptor; 6 import java.lang.reflect.Method; 7 import java.util.HashMap; 8 9 /** 10 * 备忘录模式的工具类 11 * @author sxf 12 * 13 */ 14 public class BeanUtils { 15 //把bean的所有属性及数值放入到HashMap中 16 public static HashMap<String, Object> backupProp(Object bean){ 17 HashMap<String, Object> result=new HashMap<String, Object>(); 18 try { 19 //根据反射获取bean的描述 20 BeanInfo beanInfo=Introspector.getBeanInfo(bean.getClass()); 21 //获取属性描述 22 PropertyDescriptor[] descriptors=beanInfo.getPropertyDescriptors(); 23 //便利所有的属性 24 for(PropertyDescriptor descriptor:descriptors){ 25 //获取当前属性名字 26 String fieldName=descriptor.getName(); 27 //读取属性的方法 28 Method getterMethod=descriptor.getReadMethod(); 29 //读取属性的值 30 Object fieldValue=getterMethod.invoke(bean, new Object[]{}); 31 if(!fieldName.equalsIgnoreCase("class")){ 32 result.put(fieldName, fieldValue); 33 } 34 } 35 } catch (Exception e) { 36 // TODO: handle exception 37 } 38 39 return result; 40 } 41 42 //把Hashmap的值返回的bean中 43 public static void restoreProp(Object bean,HashMap<String, Object> propMap){ 44 try { 45 //获取bean的描述 46 BeanInfo beanInfo=Introspector.getBeanInfo(bean.getClass()); 47 //获得属性描述 48 PropertyDescriptor[] descriptors=beanInfo.getPropertyDescriptors(); 49 //遍历所有的属性 50 for(PropertyDescriptor des:descriptors){ 51 //属性名字 52 String fieldName=des.getName(); 53 //如果有这个属性 54 if(propMap.containsKey(fieldName)){ 55 //写属性的方法 56 Method setterMethod=des.getWriteMethod(); 57 setterMethod.invoke(bean, new Object[]{ 58 propMap.get(fieldName) 59 }); 60 } 61 } 62 } catch (Exception e) { 63 // TODO: handle exception 64 } 65 } 66 }
【4】三个备忘录的公共测试类
1 package com.yeepay.sxf.template19; 2 /** 3 * 备忘录模式测试 4 * @author sxf 5 * 6 */ 7 public class ClientTest { 8 9 public static void main(String[] args) { 10 //test01(); 11 //test02(); 12 test03(); 13 } 14 15 /** 16 * 简单的备忘录模式 17 */ 18 public static void test01(){ 19 //定义发起人 20 Originator originator=new Originator(); 21 //初始化状态 22 originator.setState("初始化"); 23 24 //定义备忘录管理员 25 Caretaker caretaker=new Caretaker(); 26 //给备忘录管理员放入备忘录 27 caretaker.setMemento(originator.createMemento()); 28 29 //模拟一系列操作修改发起者的状态 30 originator.setState("失败了"); 31 32 //经过一系列操作,回复备忘录 33 originator.restoreMemento(caretaker.getMemento()); 34 System.out.println("ClientTest.test01()"+originator.getState()); 35 } 36 37 /** 38 * 基于克隆的备忘 39 */ 40 public static void test02(){ 41 //初始化发起者 42 Originatord originatord=new Originatord(); 43 //设置初始化状态 44 originatord.setState("初始化状态"); 45 46 //创建备忘 47 originatord.createMemento(); 48 49 //经过一列操作,修改状态 50 originatord.setState("失败"); 51 52 //恢复备忘 53 originatord.resetMemento(); 54 System.out.println("ClientTest.test02()"+originatord.getState()); 55 } 56 57 /** 58 * 多状态备忘 59 */ 60 public static void test03(){ 61 //状态发起者 62 OriginatorDuo rDuo=new OriginatorDuo(); 63 //初始化状态 64 rDuo.setState1("初始化1"); 65 rDuo.setState2("初始化2"); 66 rDuo.setState3("初始化3"); 67 68 //创建备忘 69 MementoDuo duo=rDuo.createMemenTo(); 70 71 //修改状态 72 rDuo.setState1("失败1"); 73 rDuo.setState2("失败2"); 74 rDuo.setState3("失败3"); 75 76 //恢复备忘 77 rDuo.resetMemento(duo.getStateMap()); 78 79 //展示初始化 80 System.out.println("ClientTest.test03()"+rDuo.getState1()); 81 System.out.println("ClientTest.test03()"+rDuo.getState2()); 82 System.out.println("ClientTest.test03()"+rDuo.getState3()); 83 } 84 }