备忘录模式
一、定义
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
通俗地说,备忘录模式就是一个对象的备份模式,提供了一种程序数据的备份方法
备忘录模式的通用类图如下
Originator是发起人角色,记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据;Memento是备忘录角色,负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态;Caretaker是备忘录管理员角色,对备忘录进行管理、保存和提供备忘录。
备忘录的通用源码
-
发起人角色
public class Originator { //内部状态 private String state = ""; public String getState() { return state; } public void setState(String state) { this.state = state; } //创建一个备忘录 public Memento createMemento(){ return new Memento(this.state); } //恢复一个备忘录 public void restoreMemento(Memento _memento){ this.setState(_memento.getState()); } }
-
备忘录角色
public class Memento { //发起人的内部状态 private String state = ""; //构造函数传递参数 public Memento(String _state){ this.state = _state; } public String getState() { return state; } public void setState(String state) { this.state = state; } }
-
备忘录管理员角色
public class Caretaker { //备忘录对象 private Memento memento; public Memento getMemento() { return memento; } public void setMemento(Memento memento) { this.memento = memento; } }
-
场景类
public class Client { public static void main(String[] args) { //定义出发起人 Originator originator = new Originator(); //定义出备忘录管理员 Caretaker caretaker = new Caretaker(); //创建一个备忘录 caretaker.setMemento(originator.createMemento()); //恢复一个备忘录 originator.restoreMemento(caretaker.getMemento()); } }
二、备忘录模式的使用场景
-
需要提供和恢复数据的相关状态场景
-
提供一个可回滚的操作
如文档编辑的ctrl+z组合键,浏览器的后退按钮等
-
需要监控的副本场景中
例如要监控一个对象的属性,但是监控又不作为系统的主业务来调用,即使出现监控不准、错误报警也影响不大,因此一般备份一个主线程中的对象,然后由分析程序来分析
-
数据库连接的事务管理系统。
三、备忘录模式的的注意事项
-
备忘录的生命周期
备忘录创建出来要在最近的代码中使用,需要主动管理其生命周期,建立就要使用,不使用就要立即删除
-
备忘录的性能
不能在频繁建立备份的场景中使用备忘录模式(如for循环),即无法控制备忘录建立的对象数量,大对象的建立也要消耗资源
四、备忘录模式的扩展
-
clone方式的备忘录
源码
-
发起人自主备份和恢复
public class Originator implements Cloneable{ private Originator backup; //内部状态 private String state = ""; public String getState() { return state; } public void setState(String state) { this.state = state; } //创建一个备忘录 public void createMemento(){ this.backup = this.clone(); } //恢复一个备忘录 public void restoreMemento(){ //在进行恢复前应该进行断言,防止空指针 this.setState(this.backup.getState()); } //克隆当前对象 @Override protected Originator clone(){ try { return (Originator)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } }
-
场景类
public class Client { public static void main(String[] args) { //定义发起人 Originator originator = new Originator(); //建立初始状态 originator.setState("初始状态..."); System.out.println("初始状态是:"+originator.getState()); //建立备份 originator.createMemento(); //修改状态 originator.setState("修改后的状态..."); System.out.println("修改后状态是:"+originator.getState()); //恢复原有状态 originator.restoreMemento(); System.out.println("恢复后状态是:"+originator.getState()); } }
优点:程序精简了很多,而且高层模块的依赖也减少了
注意:使用clone方式的备忘录模式,适合在比较简单或比较单一的的场景中使用,尽量不要与其他对象产生严重耦合
-
-
多状态的备忘录模式(多属性?)
如下实现一个JavaBean对象的所有状态的备份和还原
类图
backupProp是把发起人的所有属性值转换到HashMap中,方便备忘录角色存储;restoreProp方法则是把HashMap中的值返回到发起人角色中。-
发起人角色
public class Originator { //内部状态 private String state1 = ""; private String state2 = ""; private String state3 = ""; 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 Memento createMemento(){ return new Memento(BeanUtils.backupProp(this)); } //恢复一个备忘录 public void restoreMemento(Memento _memento){ BeanUtils.restoreProp(this, _memento.getStateMap()); } //增加一个toString方法 @Override public String toString(){ return "state1=" +state1+"\nstat2="+state2+"\nstate3="+state3; } }
-
BeanUtils工具类
public class BeanUtils { //把bean的所有属性及数值放入到Hashmap中 public static HashMap<String,Object> backupProp(Object bean){ HashMap<String,Object> result = new HashMap<String,Object>(); try { //获得Bean描述 BeanInfo beanInfo=Introspector.getBeanInfo(bean.getClass()); //获得属性描述 PropertyDescriptor[] descriptors=beanInfo.getPropertyDescriptors(); //遍历所有属性 for(PropertyDescriptor des:descriptors){ //属性名称 String fieldName = des.getName(); //读取属性的方法 Method getter = des.getReadMethod(); //读取属性值 Object fieldValue=getter.invoke(bean,new Object[]{}); if(!fieldName.equalsIgnoreCase("class")){ result.put(fieldName, fieldValue); } } } catch (Exception e) { //异常处理 } return result; } //把HashMap的值返回到bean中 public static void restoreProp(Object bean,HashMap<String,Object> propMap){ try { //获得Bean描述 BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass()); //获得属性描述 PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors(); //遍历所有属性 for(PropertyDescriptor des:descriptors){ //属性名称 String fieldName = des.getName(); //如果有这个属性 if(propMap.containsKey(fieldName)){ //写属性的方法 Method setter = des.getWriteMethod(); setter.invoke(bean, new Object[]{propMap.get(fieldName)}); } } } catch (Exception e) { //异常处理 System.out.println("shit"); e.printStackTrace(); } } }
-
备忘录角色
public class Memento { //接受HashMap作为状态 private HashMap<String,Object> stateMap; //接受一个对象,建立一个备份 public Memento(HashMap<String,Object> map){ this.stateMap = map; } public HashMap<String,Object> getStateMap() { return stateMap; } public void setStateMap(HashMap<String,Object> stateMap) { this.stateMap = stateMap; } }
-
场景类
public class Client { public static void main(String[] args) { //定义出发起人 Originator ori = new Originator(); //定义出备忘录管理员 Caretaker caretaker = new Caretaker(); //初始化 ori.setState1("中国"); ori.setState2("强盛"); ori.setState3("繁荣"); System.out.println("===初始化状态===\n"+ori); //创建一个备忘录 caretaker.setMemento(ori.createMemento()); //修改状态值 ori.setState1("软件"); ori.setState2("架构"); ori.setState3("优秀"); System.out.println("\n===修改后状态===\n"+ori); //恢复一个备忘录 ori.restoreMemento(caretaker.getMemento()); System.out.println("\n===恢复后状态===\n"+ori); } }
通过这种方式的改造,不管有多少状态都没有问题,直接把原有的对象所有属性都备份,需要还原是可以全都还原回来。
注意:如果要设计一个在运行期决定备份状态的框架,则建议采用AOP框架来实现,避免采用动态代理增加了程序逻辑复杂性
-
-
多备份的备忘录
类图
源码-
发起人角色
public class Originator { //内部状态 private String state = ""; public String getState() { return state; } public void setState(String state) { this.state = state; } //创建一个备忘录 public IMemento createMemento(){ return new Memento(this.state); } //恢复一个备忘录 public void restoreMemento(IMemento _memento){ this.setState(((Memento)_memento).getState()); } //内置类 private class Memento implements IMemento{ //发起人的内部状态 private String state = ""; //构造函数传递参数 private Memento(String _state){ this.state = _state; } private String getState() { return state; } private void setState(String state) { this.state = state; } } }
内置类Memento全部是private的访问权限,别人无法访问到,外界如果要产生关联关系可以通过接口关联,但没法方法提供修改数据
-
备忘录的空接口
public interface IMemento { }
-
备忘录管理者
public class Caretaker { //备忘录对象 private IMemento memento; public IMemento getMemento() { return memento; } public void setMemento(IMemento memento) { this.memento = memento; } }
双接口设计,我们的一个类可以实现多个接口,在系统设计时,如果考虑对象的安全问题,则可以提供两个接口,一个是业务的正常接口,实现必要的业务逻辑,叫做宽接口;另外一个接口是一个空接口,什么方法都没有,其目的是提供给子系统外的模块访问,比如容器对象,这个叫做窄接口,由于窄接口中没有提供任何操纵数据的方法,因此相对来说比较安全
-