一.名称
二.问题(为了解决什么问题)
保存和恢复状态或操作时,可以使用这个模式,例如游戏中的保存点。
三.解决方案(主要体现在uml和核心代码上)
定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在改对象之外保存这个状态,这样以后就可将对象恢复到原先保存的状态。
说明:
备忘录是一个很特殊的对象,只有原发器对它拥有控制的权力,负责人只负责管理,而其他类无法访问到备忘录,因此我们需要对备忘录进行封装。
为了实现对备忘录对象的封装,需要对备忘录的调用进行控制,对于原发器而言,它可以调用备忘录的所有信息,允许原发器访问返回到先前状态所需的所有数据;对于负责人而言,只负责备忘录的保存并将备忘录传递给其他对象;对于其他对象而言,只需要从负责人处取出备忘录对象并将原发器对象的状态恢复,而无须关心备忘录的保存细节。理想的情况是只允许生成该备忘录的那个原发器访问备忘录的内部状态。
在实际开发中,原发器与备忘录之间的关系是非常特殊的,它们要分享信息而不让其他类知道,实现的方法因编程语言的不同而有所差异,在C++中可以使用friend关键字,让原发器类和备忘录类成为友元类,互相之间可以访问对象的一些私有的属性;在Java语言中可以将原发器类和备忘录类放在一个包中,让它们之间满足默认的包内可见性,也可以将备忘录类作为原发器类的内部类,使得只有原发器才可以访问备忘录中的数据,其他对象都无法使用备忘录中的数据。
四.例子
多状态的备忘录
/**
* Created by annuoaichengzhang on 16/3/23.
*/
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());
}
}
/**
* Created by annuoaichengzhang on 16/3/23.
*/
public class Memento {
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;
}
}
/**
* Created by annuoaichengzhang on 16/3/23.
*/
public class Caretaker {
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
/**
* Created by annuoaichengzhang on 16/3/23.
*/
public class BeanUtils {
/**
* 把bean的所有属性及数值放入到hashmap中
*/
public static HashMap<String, Object> backupProp(Object bean) {
HashMap<String, Object> result = 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, new Object[]{});
if (!fieldName.equalsIgnoreCase("class")) {
result.put(fieldName, fieldValue);
}
}
} catch (IntrospectionException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return result;
}
public static void restoreProp(Object bean, HashMap<String, Object> propMap) {
try {
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor descriptor : descriptors) {
String fieldName = descriptor.getName();
if (propMap.containsKey(fieldName)) {
Method setter = descriptor.getWriteMethod();
setter.invoke(bean, new Object[]{propMap.get(fieldName)});
}
}
} catch (IntrospectionException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
/**
* Created by annuoaichengzhang on 16/3/23.
*/
public class Client {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
// 初始化
originator.setState1("中国");
originator.setState2("强盛");
originator.setState3("繁荣");
// 初始化状态
System.out.println("初始化状态======" + originator.getState1());
caretaker.setMemento(originator.createMemento());
// 修改状态值
originator.setState1("架构");
originator.setState2("软件");
originator.setState3("优秀");
// 恢复一个备忘录
originator.restoreMemento(caretaker.getMemento());
// 恢复后状态
System.out.println("恢复后状态======" + originator.getState1());
}
}
其实android中可以把BeanUtil方法替换为对象保存为json字符串,用时再解析为对象比较好。
五.效果(有啥优缺点)
优点
1. 它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
2. 备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。
缺点
资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。
常用案例
可悔棋的中国象棋
rpg游戏恢复点,如果后续玩家不行牺牲,可以在这个恢复点重新开始游戏。