备忘录模式
备忘录模式 |
- 备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态(其实不一定是在对象之外,可以是类自主备份和恢复),这样以后就可以将该对象恢复到原先保存的状态。
- 通俗的说备忘录模式就是一个对象的备份模式,提供了一种程序数据的备份方法,其通用类图如下:
-
- Originator发起人角色:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。
- Memento备忘录角色:负责存储Originator发起人的内部状态,在需要时提供发起人需要的内部状态。
- Caretaker备忘录管理员角色:对备忘录进行管理,保存和提供备忘录。
- 其通用类图的源码如下:
-
public class Originator { private String state = "very goood"; public void setState(String newState){ this.state = newState; } public String getState(){ return this.state; } public Memento createMemento(){ return new Memento(this.state); } public void restoreState(Memento _memento){ this.setState(_memento.getState()); } } public class Memento { private String state; public Memento(String state){ this.state = state; } public String getState(){ return this.state; } } public class Caretaker { private Memento _memento = null; public void setMemento(Memento memento){ this._memento = memento; } public Memento getMemento(){ return this._memento; } } public class Client { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Originator originator = new Originator(); Caretaker _caretaker = new Caretaker(); _caretaker.setMemento(originator.createMemento()); originator.setState("very bad"); System.out.println(originator.getState()); originator.restoreState(_caretaker.getMemento()); System.out.println(originator.getState()); } }
- 由于备忘录模式有很多变形和处理,每种方式都有自身的优点和缺点,标准模式很难在项目中遇到,基本上都是一些变换处理。备忘录模式主要适用于:1、需要保存和恢复数据的场景;2、提供一个可回滚的操作,如win常见的Ctrl+Z,浏览器中的后退按钮,文本管理器上的backspace键等;3、需要监控副本的场景。例如监控一个对象的属性,但是监控又不应该作为系统的主要业务来调用,即使出现监控不准、错误报警也影响不大,因此一般的做法是备份一个主线程中的对象,然后由分析程序分析;4、数据库连接的事务管理就是用的备忘录模式。
备忘录模式的扩展 |
- clone方式的备忘录模式
在原型模式中,我们通过clone的方式产生一个对象的内部状态,这也是很好的一个备份方式,发起人角色只要实现Cloneable就成,其通用类图如下:
public class Originator implements Cloneable{ private String state = "very good"; public void setState(String newState){ this.state = newState; } public String getState(){ return this.state; } public Originator createMemento(){ return this.clone(); } public void restoreState(Originator originator){ this.setState(originator.getState()); } @Override protected Originator clone(){ try { return (Originator) super.clone(); } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } } public class Caretaker { private Originator originator = null; public void setOriginator(Originator originator){ this.originator = originator; } public Originator getOriginator(){ return this.originator; } } public class Client { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Originator _originator = new Originator(); Caretaker caretaker = new Caretaker(); caretaker.setOriginator(_originator.createMemento()); _originator.setState("very bad"); System.out.println(_originator.getState()); _originator.restoreState(caretaker.getOriginator()); System.out.println(_originator.getState());; } }
当然了发起人还可以自主备份和恢复,具体代码如下:
public class Originator3 implements Cloneable{ private Originator3 backup = null; private String state = "very bad"; public void setState(String newState){ this.state = newState; } public String getState(){ return this.state; } public void createMemento(){ this.backup = this.clone(); } public void restoreState(){ this.setState(this.backup.getState()); } protected Originator3 clone(){ try { return (Originator3) super.clone(); } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } }
- 多状态的备忘录模式
如上我们讲解的都是单状态的情况,而在实际的开发中一个对象很可能有很多状态,一个JavaBean有多个属性是很常见的。因此我们讲解一个对象全状态备份方案,他有多种处理方式,比如使用clone的方式就可以解决,使用数据技术也可以解决(DTO回写到临时表中)等,如下方法是实现一个JavaBean对象的所有状态的备份和还原,类图如下所示:
public class Originator4 { private String state1 = null; private String state2 = null; private String state3 = null; public String getState1() { return state1; } public void setState1(String state1) { this.state1 = state1; } public String getState2() { return state2; } public void setState2(String state2) { this.state2 = state2; } public String getState3() { return state3; } public void setState3(String state3) { this.state3 = state3; } public Memento4 createMemento(){ return new Memento4(BeanUtils.backup(this)); } public void restoreMemento(Memento4 _memento){ BeanUtils.restoreMap(this, _memento.getStateMap()); } } public class BeanUtils { public static HashMap<String,Object> backup(Object bean){ HashMap<String,Object> map = new HashMap<String,Object>(); try { BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass()); PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors(); for(PropertyDescriptor des : descriptors){ //读取属性的名称 String fileName = des.getName(); //读取属性的方法 Method getter = des.getReadMethod(); Object fieldValue = getter.invoke(bean, new Object[]{}); if(!fileName.equalsIgnoreCase("class")) map.put(fileName, fieldValue); } } catch (IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } return map; } public static void restoreMap(Object bean, HashMap<String,Object> map){ try { BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass()); //获取属性描述 PropertyDescriptor[] descriptor = beanInfo.getPropertyDescriptors(); for(PropertyDescriptor des : descriptor){ //属性名称 String fileName = des.getName(); if(map.containsKey(fileName)){ Method setter = des.getWriteMethod(); setter.invoke(bean, new Object[]{map.get(fileName)}); } } } catch (IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public class Memento4 { private HashMap<String,Object> stateMap; public Memento4(HashMap<String,Object> map){ this.stateMap = map; } public HashMap<String,Object> getStateMap(){ return this.stateMap; } } public class Caretaker4 { private Memento4 _memento = null; public void setMemento(Memento4 memento){ this._memento = memento; } public Memento4 getMemento(){ return this._memento; } } public class Client { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Originator4 originator = new Originator4(); Caretaker4 caretaker = new Caretaker4(); caretaker.setMemento(originator.createMemento()); originator.setState1("haha"); originator.setState2("heihei"); originator.setState3("hehe"); originator.restoreMemento(caretaker.getMemento()); } }
- 多备份的备忘录
如上的解决方法中还不能做到将备份的上一个状态还原,因为如上的每个对象只有个备份,那么我们把对象的状态还原到某个点怎么办呢?这就需要对每一个对象进行多备份。首先我们介绍一个名词,检查点(Check Point),也就是备份的时候的戳记,系统级的备份一般是时间戳,如下在设计的时候检查点是一个唯一的标识字符串。只需要对多状态备忘录模式中的Caretaker中的代码做如下修改就可以,修改后的代码如下:
public class Caretaker4 { private HashMap<String,Memento4> _mementoMap = new HashMap<String,Memento4>(); public void setMemento(String id,Memento4 memento){ this._mementoMap.put(id, memento); } public Memento4 getMemento(String id){ return this._mementoMap.get(id); } } public class Client { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Originator4 originator = new Originator4(); Caretaker4 caretaker = new Caretaker4(); caretaker.setMemento("001",originator.createMemento()); originator.setState1("haha"); originator.setState2("heihei"); originator.setState3("hehe"); caretaker.setMemento("002", originator.createMemento()); originator.restoreMemento(caretaker.getMemento("001")); } }
- 设计具有更好的封装性