举个栗子
问题描述
打游戏存进度。
简单实现
GameRole
/**
* 游戏角色
* Created by callmeDevil on 2019/8/11.
*/
public class GameRole {
// 生命力
private int vit;
// 攻击力
private int atk;
// 防御力
private int def;
// 状态显示
public void stateDisplay() {
System.out.println("角色当前状态:");
System.out.println(String.format("体力:%s", this.vit));
System.out.println(String.format("攻击力:%s", this.atk));
System.out.println(String.format("防御力:%s", this.def));
System.out.println();
}
// 获得初始状态
public void getInitState() {
// 数据通常来自本机磁盘或远程数据库
this.vit = 100;
this.atk = 100;
this.def = 100;
}
// 战斗
public void fight(){
// 在与Boss大战后游戏数据损耗为0
this.vit = 0;
this.atk = 0;
this.def = 0;
}
// 省略 get set
}
测试
public class Test {
public static void main(String[] args) {
// 大战Boss前
GameRole lufi = new GameRole();
// 获得初始角色状态
lufi.getInitState();
lufi.stateDisplay();
// 通过“游戏角色”新实例,保存进度
GameRole backup = new GameRole();
backup.setVit(lufi.getVit());
backup.setAtk(lufi.getAtk());
backup.setDef(lufi.getDef());
// 大战Boss时,损耗严重,全部为0
lufi.fight();
lufi.stateDisplay();
// GameOver不甘心,恢复进度,重新玩过
lufi.setVit(backup.getVit());
lufi.setAtk(backup.getAtk());
lufi.setDef(backup.getDef());
lufi.stateDisplay();
}
}
测试结果
角色当前状态:
体力:100
攻击力:100
防御力:100
角色当前状态:
体力:0
攻击力:0
防御力:0
角色当前状态:
体力:100
攻击力:100
防御力:100
存在问题
在客户端调用这段,把整个游戏角色的细节暴露了,职责太大,需要知道游戏角色的生命力、攻击力、防御力这些细节,还要进行备份。如果以后需要增加“魔法力”或修改现有的某种力,那这部分代码就需要修改,同样恢复时也是一样的问题。
备忘录模式
定义
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
UML图
代码实现
GameRole
/**
* 游戏角色
* Created by callmeDevil on 2019/8/11.
*/
public class GameRole {
// 属性与简单实现GameRole相同
// 保存角色状态
public RoleStateMemento saveState() {
return new RoleStateMemento(vit, atk, def);
}
// 恢复角色状态
public void recoveryState(RoleStateMemento memento) {
this.vit = memento.getVit();
this.atk = memento.getAtk();
this.def = memento.getDef();
}
// 其余方法与简单实现相同
}
RoleStateMemento
/**
* 角色状态存储类
* Created by callmeDevil on 2019/8/11.
*/
public class RoleStateMemento {
// 属性与 简单实现 GameRole 相同
// 将生命力、攻击力、防御力存入状态存储箱对象中
public RoleStateMemento(int vit, int atk, int def){
this.vit = vit;
this.atk = atk;
this.def = def;
}
// 省略 get set
}
RoleStateCaretaker
/**
* 游戏状态管理者
* Created by callmeDevil on 2019/8/11.
*/
public class RoleStateCaretaker {
private RoleStateMemento memento;
// 省略 get set
}
测试
public class Test {
public static void main(String[] args) {
// 大战Boss前
GameRole lufi = new GameRole();
lufi.getInitState();
lufi.stateDisplay();
// 保存游戏进度
RoleStateCaretaker stateAdmin = new RoleStateCaretaker();
stateAdmin.setMemento(lufi.saveState());// 将具体数据封装在了 Memento中
// 大战Boss时,损耗严重
lufi.fight();
lufi.stateDisplay();
// 恢复状态
lufi.recoveryState(stateAdmin.getMemento());
lufi.stateDisplay();
}
}
测试结果
与简单实现相同
总结
- 把要保存的细节给封装在了 Memento 中,哪一天要更改保存细节也不用影响客户端。
- 备忘录模式比较适用于功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性的一小部分时,Originator 可以根据保存的 Memento 信息还原到前一状态。
- 如果在某个系统中使用命令模式时,需要实现命令的撤销功能,那么命令模式可以使用备忘录模式来存储可撤销操作的状态。
- 使用备忘录可以把复杂的对象内部信息对其他的对象屏蔽起来。