编程中的后悔药——备忘录模式
备忘录模式的定义
在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样,以后就可以将该对象恢复到原先保存的状态。
备忘录模式的使用场景
- 需要保存一个对象在某一时刻的状态或部分状态。
- 如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界访问其内部状态,通过中间对象可以间接访问其内部对象。
备忘录模式的实例
角色介绍
Originator:负责创建一个备忘录,可以记录、恢复自身的内部状态。
Memento:备忘录角色,用于存储Originator的内部状态,并且可以防止Originator以外的对象访问Memento。
Caretaker:负责存储备忘录,不能对备忘录的内容进行操作和访问,只能够将备忘录传递给其他对象。
备忘录对象
Memento.class
/**
* description: 备忘录类
*/
public class Memento {
public int checkpoint;
public int lifeValue;
public String weapon;
public int getCheckpoint() {
return checkpoint;
}
public void setCheckpoint(int checkpoint) {
this.checkpoint = checkpoint;
}
public int getLifeValue() {
return lifeValue;
}
public void setLifeValue(int lifeValue) {
this.lifeValue = lifeValue;
}
public String getWeapon() {
return weapon;
}
public void setWeapon(String weapon) {
this.weapon = weapon;
}
@Override
public String toString() {
return "Memento{" +
"checkpoint=" + checkpoint +
", lifeValue=" + lifeValue +
", weapon='" + weapon + '\'' +
'}';
}
}
Originator对象
Game.class
/**
* description: Originator对象,存储关卡、人物的生命值、武器
* 在该类中可以通过createMemento函数来创建该用户的备忘录对象。
*/
public class Game {
private static final String TAG = "Game";
private int checkpoint = 1;
private int lifeValue = 100;
private String weapon = "狼牙棒";
public void play() {
Log.i(TAG, "玩游戏:" + String.format("第%d关", checkpoint) + "奋战杀敌中");
lifeValue -= 10;
Log.i(TAG, "进度升级啦");
checkpoint++;
Log.i(TAG, "到达 " + String.format("第%d关", checkpoint));
}
public void quit() {
Log.i(TAG, "-------------");
Log.i(TAG, "退出前的游戏属性:" + this.toString());
Log.i(TAG, "退出游戏");
Log.i(TAG, "-------------");
}
public Memento createMemento() {
Memento memento = new Memento();
memento.checkpoint = checkpoint;
memento.lifeValue = lifeValue;
memento.weapon = weapon;
return memento;
}
public void restore(Memento memento) {
this.checkpoint = memento.checkpoint;
this.lifeValue = memento.lifeValue;
this.weapon = memento.weapon;
Log.i(TAG, "恢复后的游戏属性:" + this.toString());
}
@Override
public String toString() {
return "Game{" +
"checkpoint=" + checkpoint +
", lifeValue=" + lifeValue +
", weapon='" + weapon + '\'' +
'}';
}
}
存储备忘录类
Caretaker.class
/**
* description: Caretaker,负责管理Memento
*/
public class Caretaker {
private Memento memento;
public void archive(Memento memento) {
this.memento = memento;
}
public Memento getMemento() {
return memento;
}
}
Client.class
public class Client {
public void test() {
//构建游戏对象
Game game = new Game();
//1.打游戏
game.play();
Caretaker caretaker = new Caretaker();
//2.游戏存档
caretaker.archive(game.createMemento());
//3.退出游戏
game.quit();
//恢复游戏
Game newGame = new Game();
newGame.restore(caretaker.getMemento());
}
}
Game在这里为Originator角色,也就是需要存储数据的对象,在这里并没有直接存储Game的对象,而是通过Memento对Game对象的数据进行存储,然后再存储Memento对象,最终对Memento的存取操作则交给Caretaker对象。在这个过程中,各个角色职责清晰、单一,代码也比较简单,即对外屏蔽了对Game角色的直接访问,在满足了对象状态存取功能的同时也使得该模块的结构保持清晰、整洁。
小结
备忘录模式是在不破坏封装的条件下,通过备忘录对象(Memento)存储另外一个对象内部状态的快照,在将来合适的时候把这个对象还原到存储起来的状态。
优点
- 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
- 实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点
消耗资源,如果类的成员变量过多,势必会占用较大的资源,而且每一次保存都会消耗一定内存。