设计模式-备忘录模式
"谁说破镜不能重圆?"
现实例子
对于大多数的单机类游戏来说,
由于故事线太长,游戏内容太丰富,可能因为游玩时间不够或者想要得到更好的游戏体验 ,
游戏玩家很难一次就满意地通关
因此就产生了经典的存档读档模式,
玩家只需在某个时刻进行存档,那么后续哪怕死亡,只需要读挡就会瞬间复活
这里的存档就是备忘录
代码结构
原发器Originator
需要保存状态的类,如游戏角色
- 各种内部属性(包含了需要保存的状态属性)
- 保存状态方法
- 读取状态方法
备忘录Memento
真正保存状态的类,如保存角色的属性和状态
为方便访问,可设计为原发器的内部类
- 各种有关状态的属性
负责人Caretaker
管理备忘录的类
主要是为了避免备忘录直接暴露,因此多了一个管理类
- 备忘录
- 存储读取备忘录的方法
具体代码
根据单机游戏的角色闯关设定
与代码结构稍有不同,把备忘录从一个类变成备忘录的接口和具体备忘录
这么做的意义是可以减少属性的暴露,让负责人无法访问具体的属性
记录接口(备忘录接口)
/**
* 备忘录 -> 档案接口
* 窄接口,主要用于关闭负责人的访问属性权限
*/
public interface Record {
}
档案保存者(管理者)
/**
* 管理者 -> 档案保存者
*/
public class RecordSaver {
private Record record;
public Record getRecord() {
return record;
}
public void setRecord(Record record) {
this.record = record;
}
}
游戏角色(原发器)
/**
* Originator -> Person
* 需要保存数据的类
*/
public class Person {
String name;
Integer level;
Integer blood;
public Person(String name) {
this.name = name;
this.level = 1;
this.blood = 100;
}
public void die(){
blood = 0;
System.out.print("闯关中死亡 ");
showState();
}
public void levelUp(){
level++;
System.out.print("闯关中升级 ");
showState();
}
public void showState(){
System.out.println(name +"当前 level:"+level+" , blood:"+blood);
}
public Record save(){
System.out.println("角色存档");
return new PersonRecord(level, blood);
}
public void load(Record record){
System.out.println("角色读档");
PersonRecord personRecord = (PersonRecord)record;
blood = personRecord.blood;
level = personRecord.level;
}
}
记录(备忘录)内部类实现
/**
* Originator -> Person
* 需要保存数据的类
*/
public class Person {
// Person的属性与方法 上面已有,省略。
public void load(Record record){
System.out.println("角色读档");
PersonRecord personRecord = (PersonRecord)record;
blood = personRecord.blood;
level = personRecord.level;
}
}
调用与结果
Person ming = new Person("ming");
ming.levelUp();
RecordSaver recordSaver = new RecordSaver();
recordSaver.setRecord(ming.save());
ming.levelUp();
ming.die();
ming.load(recordSaver.getRecord());
ming.showState();
为什么不使用原型模式?
原型模式是克隆一个完整的对象,而恢复状态往往只需要部分的属性,
完全的复制可能反而会导致额外的开销,甚至恢复了一些不该恢复的属性。
适用场景
- 需要恢复类的状态到之前的某一时刻
- 部分属性需要被记录