备忘录模式
定义
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,之后就可以将该对象恢复到原先保存的状态。
备忘录模式(Memento Pattern)是一种弥补缺陷的模式,能够在失败的操作后读档
应用场景
- 需要保存和恢复数据的场景
- 需要提供一个可回滚(rollback)的操作
- 需要监控的副本场景
- 数据库连接的事务管理就是使用备忘录模式
备忘录模式的角色
- Originator发起人。记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据
- Memento备忘录角色。负责Originator当前状态的快照,之后需要时提供数据回滚
- Caretaker备忘录管理员。管理Memento角色,将发起人对备忘录角色的使用权交给管理员。
非备忘录模式
小结
- 没有备忘录角色、备忘录管理员,数据的备份靠new一个实例来记录
- new出的实例是高层模型控制,不符合封装的特点,应该将其定义容纳起来。
发起人
public class Boy {private String state;/*** 认识女孩后状态的变化*/public void changeState() {setState("心情可能很不好");}public String getState() {return state;}public void setState(String state) {this.state = state;}
}
入口类方法
/*** 非备忘录模式*/
private static void notMemento() {Boy boy = new Boy();// 初始化状态boy.setState("心情很好");System.out.println("=====男孩现在的状态=====" + boy.getState());// 记录状态Boy backup = new Boy();backup.setState(boy.getState());// 男孩去追女孩,状态改变boy.changeState();System.out.println("=====男孩追女孩之后的状态=====" + boy.getState());// 追女孩失败,恢复原状boy.setState(backup.getState());System.out.println("=====男孩恢复后的状态=====" + boy.getState());
}
结果
备忘录模式
小结
- 添加了备忘录角色Memento和备忘录管理员Caretaker
- 发起人Originator在某个时刻发起备份,管理员负责管理备份。
- 发起人发起回滚时,管理员调出先前记录的备忘录,提供给发起人以回滚。
UML图
发起人——Originator
/*** 发起人*/
public class Man {private String state;/*** 认识女孩后状态的变化*/public void changeState() {setState("心情可能很不好");}public String getState() {return state;}public void setState(String state) {this.state = state;}/*** 保存一个备份** @return 备忘录*/public Memento createMemento() {return new Memento(this.state);}/*** 恢复一个备份*/public void restoreMemento(Memento memento) {this.state = memento.getState();}
}
备忘录角色——Memento
/*** 备忘录角色*/
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;}
}
备忘录管理员——Caretaker
/*** 备忘录管理员*/
public class Caretaker {/*** 备忘录角色*/private Memento memento;public Memento getMemento() {return memento;}public void setMemento(Memento memento) {this.memento = memento;}
}
入口类方法
/*** 加入备忘录管理员*/
private static void mementoByCaretaker() {Man man = new Man();// 创建备忘录管理员Caretaker caretaker = new Caretaker();// 初始化状态man.setState("心情很好");System.out.println("=====男孩现在的状态=====" + man.getState());// 记录状态caretaker.setMemento(man.createMemento());// 男孩去追女孩,状态改变man.changeState();System.out.println("=====男孩追女孩之后的状态=====" + man.getState());// 追女孩失败,恢复原状man.restoreMemento(caretaker.getMemento());System.out.println("=====男孩恢复后的状态=====" + man.getState());
}
结果
拓展:clone方式的备忘录
小结
- 结合原型模式clone的特点,在发起人内部重写clone()方法
- 发起人除了自身的状态外,还维护一个副本对象,当发起人发起备份时,会将clone出的对象复制给副本对象
- 使用clone方式的备忘录可以舍弃备忘录对象Memento和备忘录管理员Caretaker
发起人——Originator
/*** 融合备忘录的发起人角色*/
public class Originator implements Cloneable {/*** 内部状态*/private String state;/*** 自主备份状态*/private Originator backup;/*** 认识女孩后状态的变化*/public void changeState() {setState("心情可能很不好");}public String getState() {return state;}public void setState(String state) {this.state = state;}/*** 保存一个备份*/public void createMemento() {try {this.backup = this.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}}/*** 恢复一个备份*/public void restoreMemento() {this.state = Optional.of(this.backup).orElseGet(Originator::new).state;}@Overrideprotected Originator clone() throws CloneNotSupportedException {return (Originator) super.clone();}
}
入口类方法
/*** 使用克隆完成备忘*/
private static void mementoByClone() {// 定义发起人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());
}
结果
拓展:多状态的备忘录模式
小结
- 多状态下会引入map来备份bean对象的fieldName和fieldValue
- 使用到java.beans下的反射工具Introspectors获取到bean的getter、setter方法获取bean的属性
- Clone方式的备忘录,适合在单一场景下使用,不适合在与其他对象有强耦合的场景(因为没有备忘录角色和备忘录管理员的参与)。
- 多状态的备忘录模式则分工明确,但是使用反射来装载bean会降低一些效率
发起人
/*** 多状态 发起人角色*/
public class OriginatorMulState {private String state1;private String state2;private String state3;/*** 创建一个备忘录** @return 备忘录*/public MementoMul createMemento() {return new MementoMul(BeanUtils.backupProp(this));}public void restoreMemento(MementoMul mementoMul) {BeanUtils.restoreProp(this, mementoMul.getStateMap());}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;}@Overridepublic String toString() {return "OriginatorMulState{" +"state1='" + state1 + '\'' +", state2='" + state2 + '\'' +", state3='" + state3 + '\'' +'}';}
}
备忘录角色
/*** 备忘录角色 多状态*/
public class MementoMul {private Map<String, Object> stateMap;public MementoMul(Map<String, Object> stateMap) {this.stateMap = stateMap;}public Map<String, Object> getStateMap() {return stateMap;}public void setStateMap(Map<String, Object> stateMap) {this.stateMap = stateMap;}
}
备忘录管理员
/*** 多状态 备忘录管理员*/
public class CaretakerMulState {/*** 备忘录角色*/private MementoMul memento;public MementoMul getMemento() {return memento;}public void setMemento(MementoMul memento) {this.memento = memento;}
}
Bean工具类
/*** Bean工具类*/
public class BeanUtils {/*** 把bean的所有属性放入到Map中** @param bean 待备忘的对象* @return 备忘对象的map*/public static <T> Map<String, Object> backupProp(T bean) {Map<String, Object> map = new HashMap<>();try {BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();for (PropertyDescriptor descriptor : descriptors) {String fieldName = descriptor.getName();Method getter = descriptor.getReadMethod();Object fieldValue = getter.invoke(bean);if (!fieldName.equalsIgnoreCase("class")) {map.put(fieldName, fieldValue);}}} catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {e.printStackTrace();}return map;}public static <T> void restoreProp(T bean, Map<String, Object> map) {try {BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();for (PropertyDescriptor descriptor : descriptors) {String fieldName = descriptor.getName();if (map.containsKey(fieldName)) {Method setter = descriptor.getWriteMethod();setter.invoke(bean, map.get(fieldName));}}} catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {e.printStackTrace();}}
}
入口类方法
/*** 多状态 备忘录*/
private static void mulState() {OriginatorMulState originatorMulState = new OriginatorMulState();CaretakerMulState caretaker = new CaretakerMulState();originatorMulState.setState1("Take");originatorMulState.setState2("it");originatorMulState.setState3("boy");System.out.println("初始化状态:" + originatorMulState);caretaker.setMemento(originatorMulState.createMemento());// 修改状态originatorMulState.setState1("玩");originatorMulState.setState2("游");originatorMulState.setState3("戏");System.out.println("修改后的状态:" + originatorMulState);// 恢复状态originatorMulState.restoreMemento(caretaker.getMemento());System.out.println("恢复后的状态:" + originatorMulState);
}
结果
参考书籍
秦小波《设计模式之禅》