一、定义
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
二、角色
Originator
发起人角色
记录当前时刻的状态,负责定义哪些属于需要备份的状态,负责创建和恢复备忘录数据。Memento
备忘录角色
负责存储发起人的状态Caretaker
备忘录管理员角色
对备忘录进行管理和保存
三、使用场景
- 需要保存和恢复数据的相关状态场景。
- 提供一个可回滚(rollback)的操作。
- 数据库连接的事务管理就是用的备忘录模式。
四、注意事项
- 备忘录创建出来就要在“最近”的代码中使用,要主动管理它的生命周期,建立就要使用,不使用就要立刻删除其引用,等待垃圾回收器对它的回收处理。
- 不要在频繁建立备份的场景中使用备忘录模式(比如一个for循环中),原因有二:一是控制不了备忘录建立的对象数量;二是大对象的建立是要消耗资源的,系统的性能需要考虑。
五、实例分析
1. clone 方式的备忘录
使用原型模式,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(Originator _originator) { if (this.backup != null) { this.setState(this.backup.getState()); } } // 克隆当前对象 @Override protected Originator clone() { try { return (Originator) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } }
这里的这一个类饰演了 Originator
、Memento
和 Caretaker
三个角色。
注意:使用 clone 方式进行的备份尽量针对较简单的场景,尽量不要和其它对象存在严重的耦合关系。
2. 多状态的备忘录模式
我们可以使用 HashMap
来存储 Originator
的多个状态信息。为什么不直接使用拷贝,也是为了避免破坏发起人的通用性,且在做恢复操作的时候需要对对象进行多次赋值操作,也容易产生错误。
2.1 首先定义一个发起人角色
public class Originator implements Cloneable { // 内部状态 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; } }
2.2 这里我们使用到了 BeanUtils
这样一个工具类
import java.beans.BeanInfo; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.util.HashMap; 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(); } } }
2.3 备忘录角色
import java.util.HashMap; 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; } }
2.4 备忘录管理员
public class Caretaker { // 备忘录对象 private Memento memento; public Memento getMemento() { return memento; } public void setMemento(Memento memento) { this.memento = memento; } }
2.5 场景
public class Main { 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); } }
2.6 执行结果
===初始化状态=== state1=中国 stat2=强盛 state3=繁荣 ===修改后状态=== state1=软件 stat2=架构 state3=优秀 ===恢复后状态=== state1=中国 stat2=强盛 state3=繁荣
3. 多备份的备忘录
之前我们的情况都是只有一份备份,假如现在需要多个备份,那么该怎么做呢。
其实我们只需要把上面的 Caretaker
管理员稍作改动就行,如下
import java.util.HashMap; 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); } }
注意:
该备份一旦产生就装入内存,没有任何销毁的意向,这是非常危险的,很可能导致内存溢出。因此,在系统设计时,要严格限定备忘录的创建,例如增加Map的上限。
当然,对于备份,我们也可以选择本地化保存。
查看更多:设计模式分类以及六大设计原则