备忘录模式的原理
备忘录模式,也叫快照(Snapshot)模式,英文翻译是 Memento Design Pattern。在 GoF 的《设计模式》一书中,备忘录模式是这么定义的:
Captures and externalizes an object’s internal state so that it can be restored later, all without violating encapsulation.
翻译成中文就是:在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。
需求
游戏角色有攻击力和防御力,在大战boss前,大战两分钟,四分钟,保存自身的状态,当大战Boss后攻击力和防御力下降,从备忘录对象恢复到大战前状态
备忘录模式经常可以遇到,譬如下面这些场景:
-
浏览器回退:浏览器一般有浏览记录,当我们在一个网页上点击几次链接之后,可在左上角点击左箭头回退到上一次的页面,然后也可以点击右箭头重新回到当前页面
-
数据库备份与还原:一般的数据库都支持备份与还原操作,备份即将当前已有的数据或者记录保留,还原即将已经保留的数据恢复到对应的表中
-
编辑器撤销与重做:在编辑器上编辑文字,写错时可以按快捷键 Ctrl + z 撤销,撤销后可以按 Ctrl + y 重做
-
虚拟机生成快照与恢复:虚拟机可以生成一个快照,当虚拟机发生错误时可以恢复到快照的样子
-
Git版本管理:Git是最常见的版本管理软件,每提交一个新版本,实际上Git就会把它们自动串成一条时间线,每个版本都有一个版本号,使用 git reset --hard 版本号 即可回到指定的版本,让代码时空穿梭回到过去某个历史时刻
-
棋牌游戏悔棋:在棋牌游戏中,有时下快了可以悔棋,回退到上一步重新下
- originator: 对象(需要保存状态的对象)
- Memento: 备忘录对象,负责保存好记录,即originator的状态
- Caretaker:守护者对象,负责保存多个备忘录对象,使用集合管理,提高效率
package com.evan.memento;
import lombok.Data;
import java.util.HashMap;
import java.util.ArrayList;
/**
* 这是一个守护者对象,保存游戏角色的状态
*/
@Data
public class Caretaker {
//如果只保存一次状态可以使用
private Memento memento;
// 保存GameRole多个状态
private ArrayList<Memento> mementos;
// 保存多个GameRole多个状态
private HashMap<String, ArrayList<Memento>> rolesMementos;
}
package com.evan.memento;
import lombok.Data;
@Data
public class GameRole {
// 攻击力
private int vit;
// 防御力
private int def;
// 创建Memento 即根据当前的状态得到Memento
public Memento createMemento() {
return new Memento(vit, def);
}
// 从备忘录中得到GameRole的状态
public void recoverGameRoleMemento(Memento memento) {
this.vit = memento.getVit();
this.def = memento.getDef();
}
public void display() {
System.out.println("游戏角色当前的攻击力:" + this.vit + "防御力" + this.def);
}
}
package com.evan.memento;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Memento {
// 攻击力
private int vit;
// 防御力
private int def;
}
package com.evan.memento;
import java.util.ArrayList;
import java.util.Random;
public class Client {
public static void main(String[] args) {
// 创建游戏角色
GameRole gr = new GameRole();
gr.setDef(100);
gr.setVit(100);
System.out.println("和boss 大战前的状态");
gr.display();
// 把当前状态保存到Caretaker对象种
Caretaker caretaker = new Caretaker();
ArrayList<Memento> mementos = new ArrayList();
mementos.add(gr.createMemento());
System.out.println("和boss 大战两分钟");
gr.setDef(70);
gr.setVit(70);
mementos.add(gr.createMemento());
gr.display();
System.out.println("和boss 大战四分钟");
gr.setDef(50);
gr.setVit(50);
mementos.add(gr.createMemento());
gr.display();
caretaker.setMementos(mementos);
System.out.println("和boss 大战后,随机恢复");
Random r = new Random ();
int i = r.nextInt(3);
gr.recoverGameRoleMemento(caretaker.getMementos().get(i));
System.out.println("恢复后的状态");
gr.display();
}
}