Memento模式也叫备忘录模式,是行为模式之一,它的作用是保存对象的内部状态,并在需要的时候(undo/rollback)恢复对象以前的状态。
如果一个对象需要保存状态并可通过undo或rollback等操作恢复到以前的状态时,可以使用Memento模式。
Originator(原生者): 需要被保存状态以便恢复的那个对象。
Memento(备忘录): 该对象由Originator创建,主要用来保存Originator的内部状态。
Caretaker(管理者): 负责在适当的时间保存/恢复Originator对象的状态。
超级玛丽奥的游戏推出了新版本,可以在中途保存游戏进度,以便下次继续。
首先我们定义一个接口,用来在游戏中定义存档。
// An highlighted block
package design.memo.gys.memo;
public interface MemoInfo {
}
所有需要存档的游戏都需要实现该接口,并且将备忘录类私有化成游戏内部。
超级马里奥1是Jim开发的,他使用Hashmap来保存游戏的状态,因此,备忘录必须匹配该游戏状态。
// An highlighted block
package design.memo.gys.memo;
import java.util.HashMap;
public class GameA {
private HashMap<String, String> state;
public GameA() {
state=new HashMap<>();
}
public MemoInfo creatMemo() {
return new Memoto(state);
}
public void reStoreMemo(MemoInfo memoinfo) {
this.state=((Memoto)memoinfo).getState();
}
public void showState() {
System.out.println("Current state:"+state.toString());
}
public void test1() {
state=new HashMap<>();
state.put("Gate", "3");
state.put("blood", "500");
state.put("Enemy", "5");
}
public void test2() {
state=new HashMap<>();
state.put("Gate", "4");
state.put("blood", "1");
state.put("Enemy", "3");
}
private class Memoto implements MemoInfo{
private HashMap<String, String> state;
public Memoto(HashMap<String, String> state) {
super();
this.state = new HashMap(state);
}
public HashMap<String,String> getState(){
return state;
}
public void setState(HashMap state) {
this.state=state;
}
}
}
该程序内部提供了一个状态显示函数并且给出了两个测试函数,可以看到,在private的Memoto类中,该类的构造器使用的是
// An highlighted block
public Memoto(HashMap<String, String> state) {
super();
this.state = new HashMap(state);
}
代码中重新创建了新的Hashmap,并将原来的状态作为参数传入,如果不这样做,得到的知识原来状态的新的引用,无法作为备忘录进行存储并恢复。
超级马里奥2是由Jhon来制作完成的,在这个游戏中,使用的是ArrayList来显示状态,其余的工作和1中是一样的:
// An highlighted block
package design.memo.gys.memo;
import java.util.ArrayList;
public class GameB {
private ArrayList<String> state;
public GameB() {
state=new ArrayList<>();
}
public MemoInfo creatMemo() {
return new Memoto(state);
}
public void reStoreMemo(MemoInfo memoinfo) {
this.state=((Memoto)memoinfo).getState();
}
public void showState() {
System.out.println("Current state:"+state.toString());
}
public void test1() {
state=new ArrayList<>();
state.add("Level:4");
state.add("blood:100");
state.add("Enemy:4");
}
public void test2() {
state=new ArrayList<>();
state.add("Level:5");
state.add("blood:2");
state.add("Enemy:6");
}
private class Memoto implements MemoInfo{
private ArrayList<String> state;
public Memoto(ArrayList<String> state) {
super();
this.state = new ArrayList<>(state);
}
public ArrayList<String> getState(){
return state;
}
public void setState(ArrayList<String> state) {
this.state=state;
}
}
}
在这两个游戏中,都有产生备忘录以及获取状态的函数,现在,我们需要一个存档,将其进行保存。
// An highlighted block
package design.memo.gys.memo;
import java.util.HashMap;
public class Crack {
private HashMap<String, MemoInfo> crack;
public Crack() {
crack=new HashMap<>();
}
public void addMemo(String name,MemoInfo memo) {
crack.put(name,memo);
System.out.println("保存游戏进度...");
}
public MemoInfo getMemo(String name) {
System.out.println("恢复游戏进度...");
return crack.get(name);
}
}
在这个存档中可以保存所有马里奥系列的进度,并且能够读取各自的进度而不互相干扰。
// An highlighted block
package design.memo.gys.memo;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Crack load=new Crack();
GameA g1=new GameA();
g1.test1();
g1.showState();
load.addMemo("GameA", g1.creatMemo());
g1.test2();
g1.showState();
g1.reStoreMemo(load.getMemo("GameA"));
g1.showState();
System.out.println("-----------");
GameB g2=new GameB();
g2.test1();
g2.showState();
load.addMemo("GameB", g2.creatMemo());
g2.test2();
g2.showState();
g2.reStoreMemo(load.getMemo("GameB"));
g2.showState();
System.out.println("-----------");
try {
g2.reStoreMemo(load.getMemo("GameA"));}
catch(ClassCastException e) {
System.out.println("记录类型不匹配!!!!");
}
}
}
看一下测试结果吧。
// An highlighted block
Current state:{Enemy=5, Gate=3, blood=500}
保存游戏进度...
Current state:{Enemy=3, Gate=4, blood=1}
恢复游戏进度...
Current state:{Gate=3, blood=500, Enemy=5}
-----------
Current state:[Level:4, blood:100, Enemy:4]
保存游戏进度...
Current state:[Level:5, blood:2, Enemy:6]
恢复游戏进度...
Current state:[Level:4, blood:100, Enemy:4]
-----------
恢复游戏进度...
记录类型不匹配!!!!
可以看到,两次都顺利的恢复了各自的游戏进度,但是在A尝试读取B的进度时出现了错误,也就是说A无法解析B的内部状态,使用private内部类很好的隐藏了内部的实现细节。
优点:
缺点: