注:示例来自《大话设计模式》
假如现有如下场景 游戏的某个场景 一游戏角色有生命力 攻击力 防御力等等数据 在打Boss前和后一定会不一样的 我们允许玩家如果感觉与Boss决斗的效果不理想 可以让游戏恢复到决斗前 简单代码实现如下
游戏角色类
package Test18;
public class GameRole {
//生命力
private int vit;
//攻击力
private int atk;
//防御力
private int def;
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
//状态显示
public void StateDisplay()
{
System.out.println("角色当前状态:");
System.out.println("体力:"+this.vit);
System.out.println("攻击力:"+this.atk);
System.out.println("防御力:"+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;
}
}
客户端代码
package Test18;
public class Program {
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();
}
}
上面的写法把整个游戏角色的细节暴露给了客户端 我们应该把游戏角色的存取状态细节封装起来 下面我们使用备忘录模式进行重构 代码如下
游戏角色类
package Test18;
public class GameRole {
//生命力
private int vit;
//攻击力
private int atk;
//防御力
private int def;
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
//状态显示
public void StateDisplay()
{
System.out.println("角色当前状态:");
System.out.println("体力:"+this.vit);
System.out.println("攻击力:"+this.atk);
System.out.println("防御力:"+this.def);
}
//保存角色状态
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();
}
//获得初始状态
public void GetInitState()
{
this.vit = 100;
this.atk = 100;
this.def = 100;
}
//战斗
public void Fight()
{
this.vit = 0;
this.atk = 0;
this.def = 0;
}
}
角色状态存储箱类
package Test18;
//角色状态存储箱
public class RoleStateMemento {
//生命力
private int vit;
//攻击力
private int atk;
//防御力
private int def;
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
public RoleStateMemento(int vit, int atk, int def)
{
this.vit = vit;
this.atk = atk;
this.def = def;
}
}
角色状态管理者类
package Test18;
//角色状态管理者
public class RoleStateCaretaker {
private RoleStateMemento memento;
public RoleStateMemento getMemento() {
return memento;
}
public void setMemento(RoleStateMemento memento) {
this.memento = memento;
}
}
客户端代码
package Test18;
public class Program {
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();
}
}
备忘录模式 在不破坏封装性的前提下 捕获一个对象的内部状态 并在该对象之外保存这个状态 这样以后就可将该对象恢复到原先保存的状态
上面就是把要保存的细节给封装在了备忘录中了 哪一天要更改保存的细节也不用影响客户端了
备忘录模式比较适用于功能比较复杂的 但需要维护或记录属性历史的类 或者需要保存的属性只是众多属性中的一小部分时 发起人可以根据保存的备忘录信息还原到前一状态
如果在某个系统中使用命令模式时 需要实现命令的撤销功能 那么命令模式可以使用备忘录模式来存储可撤销操作的状态
使用备忘录可以把复杂的对象内部信息对其他的对象屏蔽起来
当角色的状态改变的时候 有可能这个状态无效 这时候就可以使用暂时存储起来的备忘录将状态复原
缺点 如果状态数据很大很多 那么在资源消耗上 备忘录对象会非常耗内存