18-备忘录模式
2018-01-22 09:23
162 查看
1.定义
备忘录模式(Memento Pattern)提供了一种弥补真实世界缺陷的方法,让“后悔药”在程序的世界中真实可行,其定义如下:Without violating encapsulation,capture and externalize an object’s internal state so that the object can be restored to this state later.(在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。)
2.类图
Originator发起人角色
记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。
Memento备忘录角色
负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。
Caretaker备忘录管理员角色
对备忘录进行管理、保存和提供备忘录。
3.代码
/** * 发起人角色 */ 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(); //创建一个备忘录 fcc8 caretaker.setMemento(originator.createMemento()); //恢复一个备忘录 originator.restoreMemento(caretaker.getMemento()); } }
4.使用场景
需要保存和恢复数据的相关状态场景。提供一个可回滚(rollback)的操作;比如Word中的CTRL+Z组合键,IE浏览器中的后退按钮,文件管理器上的backspace键等。
需要监控的副本场景中。例如要监控一个对象的属性,但是监控又不应该作为系统的主业务来调用,它只是边缘应用,即使出现监控不准、错误报警也影响不大,因此一般的做法是备份一个主线程中的对象,然后由分析程序来分析。
数据库连接的事务管理就是用的备忘录模式,想想看,如果你要实现一个JDBC驱动,你怎么来实现事务?还不是用备忘录模式嘛!
5.注意事项
备忘录的生命期备忘录创建出来就要在“最近”的代码中使用,要主动管理它的生命周期,建立就要使用,不使用就要立刻删除其引用,等待垃圾回收器对它的回收处理。
备忘录的性能
不要在频繁建立备份的场景中使用备忘录模式(比如一个for循环中),原因有二:一是控制不了备忘录建立的对象数量;二是大对象的建立是要消耗资源的,系统的性能需要考虑。因此,如果出现这样的代码,设计师就应该好好想想怎么修改架构了。
6.扩展
6.1 clone方式的备忘录
从类图上看,发起人角色融合了发起人角色和备忘录角色,具有双重功效。
/** * 融合备忘录的发起人角色 */ public class Originator implements Cloneable{ //内部状态 private String state = ""; public String getState() { return state; } public void setState(String state) { this.state = state; } //创建一个备忘录 public Originator createMemento(){ return this.clone(); } //恢复一个备忘录 public void restoreMemento(Originator _originator){ this.setState(_originator.getState()); } //克隆当前对象 @Override protected Originator clone(){ try { return (Originator)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } }
/** * 备忘录管理员角色 */ public class Caretaker { //发起人对象 private Originator originator; public Originator getOriginator() { return originator; } public void setOriginator(Originator originator) { this.originator = originator; } }
/** * 发起人自主备份和恢复 */ 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方式的备忘录模式,可以使用在比较简单的场景或者比较单一的场景中,尽量不要与其他的对象产生严重的耦合关系。
6.2 多状态的备忘录模式
/** * 发起人角色 */ 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); } }
运行结果如下所示:
===初始化状态===
state1=中国
stat2=强盛
state3=繁荣
===修改后状态===
state1=软件
stat2=架构
state3=优秀
===恢复后状态===
state1=中国
stat2=强盛
state3=繁荣
注意 如果要设计一个在运行期决定备份状态的框架,则建议采用AOP框架来实现,避免采用动态代理无谓地增加程序逻辑复杂性。
6.3 多备份的备忘录
/** * 备忘录管理员 */ public class Caretaker { //容纳备忘录的容器 private HashMap<String,Memento> memMap = new HashMap<String,Memento>(); public Memento getMemento(String idx) { return memMap.get(idx); } public void setMemento(String idx,Memento memento) { this.memMap.put(idx, memento); } }
/** * 场景类 */ public class Client { public static void main(String[] args) { //定义出发起人 Originator originator = new Originator(); //定义出备忘录管理员 Caretaker caretaker = new Caretaker(); //创建两个备忘录 caretaker.setMemento("001",originator.createMemento()); caretaker.setMemento("002",originator.createMemento()); //恢复一个指定标记的备忘录 originator.restoreMemento(caretaker.getMemento("001")); } }
注意 内存溢出问题,该备份一旦产生就装入内存,没有任何销毁的意向,这是非常危险的。因此,在系统设计时,要严格限定备忘录的创建,建议增加Map的上限,否则系统很容易产生内存溢出情况。
6.4 封装得更好一点
/** * 发起人角色 */ 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; } } }
/** * 备忘录的空接口 */ public interface IMemento { }
/** * 备忘录管理者 */ public class Caretaker { //备忘录对象 private IMemento memento; public IMemento getMemento() { return memento; } public void setMemento(IMemento memento) { this.memento = memento; } }
相关文章推荐
- 设计模式之18 - 备忘录模式Memento
- 设计模式(18) ------------备忘录模式
- 大话设计模式18----备忘录模式
- 大话设计模式学习笔记(18)——备忘录模式
- 设计模式(18)-----备忘录模式
- 设计模式(18)------备忘录模式
- C++ 18备忘录模式
- 设计模式(18)-备忘录模式
- 笔记-大话设计模式-18 备忘录模式
- 《大话设计模式》—— 读后感(18)如果再回到从前——备忘录模式
- 设计模式-18-备忘录模式
- 设计模式(18)——Memento(备忘录)
- 设计模式(18)--备忘录模式
- 18.备忘录模式
- 23种设计模式(18)-备忘录模式
- 设计模式:18 备忘录模式
- 设计模式(18)——备忘录 Memento
- JAVA设计模式-18-备忘录模式
- 18备忘录模式
- 设计模式(18)--备忘录模式