举个栗子
问题描述
打游戏存进度。
简单实现
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 信息还原到前一状态。
- 如果在某个系统中使用命令模式时,需要实现命令的撤销功能,那么命令模式可以使用备忘录模式来存储可撤销操作的状态。
- 使用备忘录可以把复杂的对象内部信息对其他的对象屏蔽起来。