备忘录模式
定义
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,之后就可以将该对象恢复到原先保存的状态。
备忘录模式(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;
}
@Override
protected 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;
}
@Override
public 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);
}
结果
参考书籍
秦小波《设计模式之禅》