一、定义
这个模式就是保存某个对象某个时刻的状态,并且通过某种方式能够使对象恢复到这个时刻的状态。
二、实例
假如你要到某个小镇,在路上你遇到个三叉路口,你不知道应该选择哪一个,因此你必须一个一个去尝试直到到达小镇。每一次尝试一条路之后,如果行不通你都要返回到三岔路口,尝试下一条路。这时候你就需要备忘录模式,记下这个三叉路口的位置。
首先定义一个walker,其中的state代表他此时的状态是回到了岔口还是尝试一条路。
public class Walker {
private String state = null ;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public MemoRandum restoreMemo() {
return new MemoRandum(this.state);
}
public void backupMemo(MemoRandum memoRandum) {
this.state = memoRandum.getState();
}
}
然后有一个备忘录类:
public class MemoRandum {
private String state = null ;
public MemoRandum(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
一个备忘录的管理类,负责备忘录的创建和记录
public class MemoManager {
private MemoRandum memoRandum = null ;
public MemoManager(MemoRandum memoRandum) {
this.memoRandum = memoRandum;
}
public MemoRandum getMemoRandum() {
return memoRandum;
}
public void setMemoRandum(MemoRandum memoRandum) {
this.memoRandum = memoRandum;
}
}
测试代码
Walker xiaoming = new Walker();
xiaoming.setState("停在岔路口");
System.out.println("========xiaoming的此时的状态=======");
System.out.println(xiaoming.getState());
System.out.println("========xiaoming保存状态之后=======");
MemoManager memoManager = new MemoManager(xiaoming.restoreMemo());
xiaoming.setState("尝试一个路口");
System.out.println(xiaoming.getState());
System.out.println("========xiaoming回复之前的状态======");
xiaoming.backupMemo(memoManager.getMemoRandum());
System.out.println(xiaoming.getState());
结果:
========xiaoming的此时的状态=======
停在岔路口
========xiaoming保存状态之后=======
尝试一个路口
========xiaoming回复之前的状态======
停在岔路口
在这个例子中,就是很简单的将walker类的一个变量在改变之前先存放到另外一个类里面,而这个存放的过程是用一个专门的类完成的。实际上,管理类的存在对于上层模块来说并不重要,我想要给walker备份的时候,不一定要通过一个专门的类,通过walker自身也是可以的。
上面的walker改成如下。其实只是把对备忘录的定义和记录放在了walker类里面而已。
public class Walker2 {
private MemoRandum memoRandum = null ;
private String state = null ;
public MemoRandum getMemoRandum() {
return memoRandum;
}
public void setMemoRandum(MemoRandum memoRandum) {
this.memoRandum = memoRandum;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public void createMemo() {
this.setMemoRandum(new MemoRandum(this.state));
}
public void backupMemo() {
this.setState(this.getMemoRandum().getState());
}
}
测试代码:
Walker2 xiaoming2 = new Walker2();
xiaoming2.setState("停在岔路口");
System.out.println("========xiaoming2的此时的状态=======");
System.out.println(xiaoming2.getState());
System.out.println("========xiaoming2保存状态之后=======");
xiaoming2.createMemo();
xiaoming2.setState("尝试一个路口");
System.out.println(xiaoming2.getState());
System.out.println("========xiaoming2回复之前的状态======");
xiaoming2.backupMemo();
System.out.println(xiaoming2.getState());
结果:
========xiaoming2的此时的状态=======
停在岔路口
========xiaoming2保存状态之后=======
尝试一个路口
========xiaoming2回复之前的状态======
停在岔路口
三、扩展
看完上面的例子,你肯定会说上面的walker只有一个属性,那如果属性很多怎么办呢,一个一个去set吗?不,当然有好的方法!我们玩闯关类游戏的时候都用过存档功能,在过一个很难的关之前先备份一下可以为自己以后省去很多麻烦。那我们就用备忘录模式来实现一下。
首先先定义一个游戏的角色类。其中createMemo和backupMemo函数负责保存和恢复,BeanUtil类是我们自己写的,接下来会说到。
public class GameRole {
private String name = null ;
private int enegy = 0;
private int blood = 0;
private MemoRandum2 memoRandum2 = new MemoRandum2() ;
public GameRole(String name,int enegy,int blood) {
this.name = name ;
this.enegy = enegy;
this.blood = blood;
}
public void createMemo() {
this.memoRandum2.setPros(BeanUtil.getHashMap(this));
}
public void backupMemo() {
BeanUtil.setHashMap(this.memoRandum2.getPros(), this);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getEnegy() {
return enegy;
}
public void setEnegy(int enegy) {
this.enegy = enegy;
}
public int getBlood() {
return blood;
}
public void setBlood(int blood) {
this.blood = blood;
}
public void print() {
System.out.println("name:"+this.name+" blood:"+this.blood+" enegy:"+this.enegy);
}
}
由于备份的数据多起来了,备忘录的类也要改变一下,我们用一个HashMap类型来存变量。
public class MemoRandum2 {
private HashMap<String, Object> pros = new HashMap<String, Object>();
public HashMap<String, Object> getPros() {
return pros;
}
public void setPros(HashMap<String, Object> pros) {
this.pros = pros;
}
}
接下来说一下BeanUtil类(很重要),这个类负责将一个类里面的属性放到HashMap里面或者将HashMap里面的属性放到一个类里面。(注意看代码中的注解)
public class BeanUtil {
/*
* 功能:将一个类里面的参数放到HashMap里面并返回这个HashMap
* 参数object:属性值得来源类,这个函数就是从object中去取属性
*/
public static HashMap<String, Object> getHashMap(Object object) {
HashMap<String, Object> result = new HashMap<String, Object>();//先定义一个HashMap
try {
BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());//取对object类的描述
//取object类里面所有属性的描述, beanInfo.getPropertyDescriptors()返回的是一个数组,这个数组里面的每一个元素都是一个对object中属性的描述类。
PropertyDescriptor[] pros = beanInfo.getPropertyDescriptors();
//遍历每一个属性
for (PropertyDescriptor des : pros) {
//取属性名
String name = des.getName();
//如果某个属性的类别不是普通变量而是一个对象的话,它的名称就是class
if(name.equals("class")){
continue;
}
//取属性的读取方法并执行
Method method = des.getReadMethod();
//invoke就是执行这个方法,执行object中的method对应的方法并将返回值赋值给value
//这个里面的new Object[]{}这个似乎没什么用啊
Object value = method.invoke(object, new Object[]{});
result.put(name, value);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
public static void setHashMap(HashMap<String, Object> map,Object object) {
try {
BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
PropertyDescriptor[] pros = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor des : pros) {
String name = des.getName();
if(name.equals("class")){
continue;
}
Method write = des.getWriteMethod();
Object value = map.get(name);
//执行object中write对应的方法,传入的参数为 new Object[]{map.get(name)}
write.invoke(object, new Object[]{map.get(name)});
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
测试代码:
GameRole crazy = new GameRole("crazy", 10, 10);
System.out.println("=====crazy初始状态=======");
crazy.print();
System.out.println("=====crazy打怪过后=======");
crazy.createMemo();
crazy.setEnegy(8);
crazy.setBlood(5);
crazy.print();
System.out.println("=====crazy恢复到先前状态======");
crazy.backupMemo();
crazy.print();
结果:
=====crazy初始状态=======
name:crazy blood:10 enegy:10
=====crazy打怪过后=======
pros4
name:crazy blood:5 enegy:8
=====crazy恢复到先前状态======
name:crazy blood:10 enegy:10
这样,一个能够保存多个属性的备忘录模式就做好了。这是你可能又会问,玩游戏的时候可以存很多个档啊,想恢复到那个就恢复到哪个。OK,我们就来实现一下。这个时候只要修改GameRole角色就好了。我们用HashMap类型来等级备忘录,并给每个备忘录写上名字。这样只要修改一下createMemo和backupMemo函数就行了。
public class GameRole2 {
private HashMap<String, MemoRandum2> memos = new HashMap<String, MemoRandum2>();
private String name = null ;
private int enegy = 0;
private int blood = 0;
public GameRole2(String name,int enegy,int blood) {
this.name = name ;
this.enegy = enegy;
this.blood = blood;
}
public void createMemo(String ids) {
MemoRandum2 memoRandum2 = new MemoRandum2();
memoRandum2.setPros(BeanUtil.getHashMap(this));
this.memos.put(ids, memoRandum2);
}
public boolean backupMemo(String ids) {
MemoRandum2 memoRandum2 = this.memos.get(ids);
if(memoRandum2 != null){
BeanUtil.setHashMap(memoRandum2.getPros(), this);
return true;
}else {
return false;
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getEnegy() {
return enegy;
}
public void setEnegy(int enegy) {
this.enegy = enegy;
}
public int getBlood() {
return blood;
}
public void setBlood(int blood) {
this.blood = blood;
}
public void print() {
System.out.println("name:"+this.name+" blood:"+this.blood+" enegy:"+this.enegy);
}
}
测试一下:
GameRole2 crazy2 = new GameRole2("crazy2", 10, 10);
System.out.println("=====crazy2初始状态=======");
crazy2.print();
crazy2.createMemo("初始");
System.out.println("=====crazy2打怪过后状态1=======");
crazy2.setBlood(8);
crazy2.setEnegy(4);
crazy2.print();
crazy2.createMemo("状态1");
System.out.println("=====crazy2打怪过后状态2=======");
crazy2.setBlood(7);
crazy2.setEnegy(2);
crazy2.print();
crazy2.createMemo("状态2");
System.out.println("=====crazy2恢复到初始状态======");
crazy2.backupMemo("初始");
crazy2.print();
System.out.println("=====crazy2恢复到状态1======");
crazy2.backupMemo("状态1");
crazy2.print();
结果:
=====crazy2初始状态=======
name:crazy2 blood:10 enegy:10
pros4
=====crazy2打怪过后状态1=======
name:crazy2 blood:8 enegy:4
pros4
=====crazy2打怪过后状态2=======
name:crazy2 blood:7 enegy:2
pros4
=====crazy2恢复到初始状态======
name:crazy2 blood:10 enegy:10
=====crazy2恢复到状态1======
name:crazy2 blood:8 enegy:4