备忘录模式
在不破坏封装性的前提下,捕获一个对象内部的状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
类结构图
Originator
负责创建一个备忘录 Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态,Originator 可根据需要决定 Memento 存储 Originator 的哪些内部状态。
Memento
负责存储 Originator 对象的内部状态,并可防止 Originator 以外的其他对象访问备忘录 Memento。
Caretaker
负责保存好备忘录 Memento,不能对备忘录的内容进行操作或检查。
代码示例
public class Originator {
private String state;
public Memento getMemento() {
return new Memento(state);
}
public void setMemento(Memento memento) {
this.state = memento.getState();
}
public void setState(String state) {
this.state = state;
}
@Override
public String toString() {
return "Originator{" +
"state='" + state + '\'' +
'}';
}
}
public class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return this.state;
}
}
public class Caretaker {
private Memento memento;
public void backupMemento(Originator originator) {
this.memento = originator.getMemento();
}
public void resetMemento(Originator originator) {
originator.setMemento(memento);
}
}
客户端示例
public class Client {
public static void main(String[] args) {
Originator o = new Originator();
o.setState("off");
System.out.println(o);
Caretaker c = new Caretaker();
c.backupMemento(o);
o.setState("on");
System.out.println(o);
c.resetMemento(o);
System.out.println(o);
}
}
运行结果
Originator{state='off'}
Originator{state='on'}
Originator{state='off'}
Process finished with exit code 0
优点
- 发起人备份自己的状态不需要自己管理,可以备份到外部,这样可以很好的保持封装的边界,这样做的意义在于可以给外部提供一个简单的操作该对象内部状态的接口。
- 发起人状态的备份与恢复,发起人自身不需要管理与操作,而是由客户端自行按需要处理。
- 如果发起人的状态出现问题,可以很轻松的恢复。
缺点
如果全部备份发起人的状态,或者其中有占用内存较大的属性(比如一个长数组),则会让备忘录模式的使用代价昂贵。
学到备忘录突然想到了一个栗子
LOL 英雄联盟相比大部分人都玩过,里面有一个英雄 “时间刺客”——艾克,他的 R (时空断裂):艾克回到过去的时间点上,他传送回4秒前所在的地方。这个大招不就很像备忘录模式吗!
那么艾克就相当于——Originator、Memento 存储的就是状态。
代码示例(匆匆拙笔,如有疏漏错误,敬请指出)
public class Ike {
private int hp;
private int mp;
private int attack;
private Thread thread;
private Memento memento;
/**
* 初始化状态
*/
public void initState() {
this.hp = 100;
this.mp = 100;
this.attack = 100;
}
/**
* 展示当前状态
*/
public void showState() {
System.out.println("艾克当前状态是:");
System.out.println("生命值: " + this.hp);
System.out.println("魔法值: " + this.mp);
System.out.println("攻击力: " + this.attack);
System.out.println();
}
public Memento saveSate() {
return new Memento(this.hp, this.mp, this.attack);
}
public void recoveryState() {
// 并且停止大招进入冷却
if (null != thread && !thread.isInterrupted()) {
thread.interrupt();
}
System.out.println("艾克开启大招!恢复状态!");
Memento memento = getMemento();
this.hp = memento.getHp();
this.mp = memento.getMp();
this.attack = memento.getAttack();
showState();
}
/**
* 进行战斗
*/
public void combat() {
System.out.println();
System.out.println("艾克进入战斗!受伤严重!");
this.hp = 1;
this.mp = 1;
this.attack = 1;
System.out.println();
}
/**
* 开启大招
*/
public void enable() {
thread = new Thread(new Runnable() {
@Override
public void run() {
while (!thread.isInterrupted()) {
System.out.println("艾克大招启动开启备份模式!" + new Date());
Memento memento = saveSate();
setMemento(memento);
try {
Thread.sleep(4_000);
} catch (InterruptedException e) {
break;
}
}
}
});
thread.start();
}
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
public class Memento {
private int hp;
private int mp;
private int attack;
public Memento(int hp, int mp, int attack) {
this.hp = hp;
this.mp = mp;
this.attack = attack;
}
public int getHp() {
return hp;
}
public int getMp() {
return mp;
}
public int getAttack() {
return attack;
}
}
客户端示例
public class Client {
public static void main(String[] args) throws InterruptedException {
Ike ike = new Ike();
ike.initState();
ike.showState();
ike.enable();
Thread.sleep(8_000);
ike.combat();
ike.showState();
ike.recoveryState();
}
}
运行结果
艾克当前状态是:
生命值: 100
魔法值: 100
攻击力: 100
艾克大招启动开启备份模式!Thu Feb 28 03:01:10 CST 2019
艾克大招启动开启备份模式!Thu Feb 28 03:01:14 CST 2019
艾克进入战斗!受伤严重!
艾克当前状态是:
生命值: 1
魔法值: 1
攻击力: 1
艾克开启大招!恢复状态!
艾克当前状态是:
生命值: 100
魔法值: 100
攻击力: 100
Process finished with exit code 0
啰啰嗦嗦一大篇文章,匆匆拙笔,如有疏漏错误,敬请指出!