人物:大鸟,小菜
事件:大鸟和小菜聊NBA火箭对爵士,最后2分落后输了的事,小菜想到如果能像游戏一样可以存档多好,打不赢再来一次,大鸟借此引出备忘录模式
备忘录模式:
1.小菜先直接用代码实现保存游戏进度
2.介绍了备忘录模式
3.结合备忘录模式实现保存游戏
用代码实现游戏存进度
游戏角色类:
@Data @Slf4j public class GameRole { /** * 生命力 */ private int vit; /** * 攻击力 */ private int atk; /** * 防御力 */ private int def; /** * 状态显示 */ public void stateDisplay() { log.info("角色当前状态:"); log.info("体力:{}", this.vit); log.info("攻击力:{}", this.atk); log.info("防御力:{}", this.def); } /** * 获得初始状态 */ public void getInitState() { this.vit = 100; this.atk = 100; this.def = 100; } /** * 战斗 */ public void fight() { this.vit = 0; this.atk = 0; this.def = 0; } }
客户端调用:
public class GameClient { public static void main(String[] args) { //大战boss前 GameRole lixiaoyao = new GameRole(); lixiaoyao.getInitState(); lixiaoyao.stateDisplay(); //保存进度 GameRole backup = new GameRole(); backup.setVit(lixiaoyao.getVit()); backup.setAtk(lixiaoyao.getAtk()); backup.setDef(lixiaoyao.getDef()); //大战boss时,损耗严重 lixiaoyao.fight(); lixiaoyao.stateDisplay(); //恢复之前状态 lixiaoyao.setVit(backup.getVit()); lixiaoyao.setAtk(backup.getAtk()); lixiaoyao.setDef(backup.getDef()); lixiaoyao.stateDisplay(); } }
输出结果:
角色当前状态: 体力:100 攻击力:100 防御力:100 角色当前状态: 体力:0 攻击力:0 防御力:0 角色当前状态: 体力:100 攻击力:100 防御力:100
大鸟:代码无措未必优,在客户端的调用时,把整个游戏细节暴露在了客户端,不足取。显然我们希望把游戏的存取细节封装起来,以体现职责分离。
备忘录模式
1.概念:在不破坏封装性地前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
2.结构图:
(1)Originator:
角色:发起人
功能:负责创建一个备忘录Memento,用来记录当前时刻它的内部状态,并可使用备忘录恢复内部状态,并能根据需要决定Memento存储Originator的哪些内部状态
(2)Memento:
角色:备忘录
功能:负责存储Originator的内部状态,并能防止Originator以外的对象访问备忘录Memento。它提供两个接口,一个是Caretaker,只能看到备忘录的窄接口,只能将备忘录传递给其他对象,另一个是Originator能看到的宽接口,允许它访问返回到先前状态所需的所有数据。
(3)Caretaker:
角色:管理者
功能:负责保存好备忘录Memento,且不能对备忘录的内容操作或检查。
3.什么时候适用于备忘录模式?
答:备忘录模式比较适用于功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性一小部分时,Originator就可以根据Memento还原到前一状态,在实际场景中如命令的撤销功能。
4.基本代码如下:
发起人(Originator类):
@Data @Slf4j public class Originator { private String state; public Memento createMemento() { return new Memento(state); } public void setMemento(Memento memento) { state = memento.getState(); } public void show() { log.info("State= " + state); } }
备忘录(Memento类):
@Data public class Memento { private String state; public Memento(String state) { this.state = state; } }
管理者(Caretaker类):
@Data public class Caretaker { private Memento memento; }
客户端程序:
public class GameClient { public static void main(String[] args) { Originator o = new Originator(); o.setState("On"); o.show(); Caretaker c = new Caretaker(); c.setMemento(o.createMemento()); o.setState("Off"); o.show(); o.setMemento(c.getMemento()); o.show(); } }
输出结果:
State= On State= Off State= On
结合备忘录模式完成游戏进度保存
1.结构图:
2.实现代码如下:
在之前的游戏角色类中,新增保存角色状态,和恢复角色状态的方法
/** * 保存角色状态 * * @return */ public RoleStateMemento saveState() { return new RoleStateMemento(vit, atk, def); } /** * 恢复角色状态 * * @param memento */ public void recoveryState(RoleStateMemento memento) { this.vit = memento.getVit(); this.atk = memento.getAtk(); this.def = memento.getDef(); }
角色状态存储箱类:
@Data public class RoleStateMemento { /** * 生命力 */ private int vit; /** * 攻击力 */ private int atk; /** * 防御力 */ private int def; public RoleStateMemento(int vit, int atk, int def) { this.vit = vit; this.atk = atk; this.def = def; } }
角色状态管理者类:
@Data public class RoleStateCaretaker { private RoleStateMemento memento; }
客户端代码(做到了隐藏了细节):
public class GameClient { public static void main(String[] args) { //大战boss前 GameRole lixiaoyao = new GameRole(); lixiaoyao.getInitState(); lixiaoyao.stateDisplay(); //保存进度 RoleStateCaretaker stateAdmin = new RoleStateCaretaker(); stateAdmin.setMemento(lixiaoyao.saveState()); //大战boss时,损耗严重 lixiaoyao.fight(); lixiaoyao.stateDisplay(); //恢复之前状态 lixiaoyao.recoveryState(stateAdmin.getMemento()); lixiaoyao.stateDisplay(); } }
注意:备忘录模式也是有缺点的,因为角色对象需要完整存储到备忘录对象中,如果状态数据很大,那么会非常消耗资源,备忘录对象会非常耗内存。