读书笔记 仅供参考
简述
Memento,备忘录模式,主要用于 Undo, Redo,History,Snapshot 等功能。事先将某个时间点的实例的状态保持下来,之后再有必要时,再将实例恢复至当时的状态。
角色和 UML
Originator
在保存自己的最新状态时生成 Memento 角色。当把以前的 Memento 角色传给 Originator 时,会自动恢复。
Memento
将 Originator 角色的内部信息整合在一起。
角色拥有以下两种接口(API):
- wide interface :所有用于获取恢复对象状态信息的方法的集合。只有 Originator 可以使用。
- narrow interface:获取有限的内部信息。
Caretaker
当 Caretaker 角色想要保存当前 Originator 角色时,通知 Originator 角色,Originator 再生成 Memento 角色返回给 Caretaker。Caretaker 只能使用 narrow interface。
UML
例子
例程是一个掷骰子游戏的程序,不同的点数会发生加钱,减钱,增加水果等情况,如果发现钱减少太多了,就回复上一次保存的状态。
// 记录主人公的状态
public class Memento {
int money;
ArrayList<String> fruits;
Memento(int money) {
this.money = money;
fruits = new ArrayList<>();
}
void addFruits(String fruit) {
fruits.add(fruit);
}
public int getMoney() {
return money;
}
List<String> getFruits() {
return (List<String>) fruits.clone();
}
}
// 表示游戏主人公
public class Gamer {
private int money; //所持金钱
private List<String> fruits = new ArrayList<>(); //所获得的水果
private Random random = new Random(); //随机数生成器
private static String[] fruitsName = {
"苹果", "葡萄", "香蕉", "橘子",
};
public Gamer(int money) {
this.money = money;
}
public int getMoney() {
return money;
}
// 掷筛子进行游戏
public void bet() {
int dice = random.nextInt(6) + 1;
if (dice == 1) { //结果为一,增加金钱
money += 100;
System.out.println("金钱增加了");
} else if (dice == 2) {
money /= 2;
System.out.println("金钱减半");
} else if (dice == 6) {
String f = getFruit();
System.out.println("获得水果(" + f + ")");
} else {
System.out.println("什么都没有发生");
}
}
// 拍摄快照
public Memento createMemento() {
Memento m = new Memento(money);
Iterator<String> it = fruits.iterator();
while (it.hasNext()) {
String f = it.next();
if (f.startsWith("好吃的")) {
m.addFruits(f);
}
}
return m;
}
// 撤销
public void restoreMemento(Memento memento ) {
this.money = memento.money;
this.fruits = memento.fruits;
}
@Override
public String toString() {
return "Gamer{" +
"money=" + money +
", fruits=" + fruits +
", random=" + random +
'}';
}
private String getFruit() {
String prefix = "";
if(random.nextBoolean()) {
prefix = "好吃的";
}
return prefix + fruitsName[random.nextInt(fruitsName.length)];
}
}
public class Main {
public static void main(String[] args) {
Gamer gamer = new Gamer(100);
Memento memento = gamer.createMemento();
for (int i = 0; i < 100; i++) {
System.out.println("==== " + i); //掷筛子的次数
System.out.println(" 当前状态:" + gamer);
gamer.bet(); //进行游戏
System.out.println("所持金钱为:" + gamer.getMoney() + " 元。");
if (gamer.getMoney() > memento.getMoney()) {
System.out.println(" (所持金额增加了,因此保存当前的状态。)");
memento = gamer.createMemento();
} else if (gamer.getMoney() < memento.getMoney() / 2) {
System.out.println(" 所持金钱减少了,回复至之前的状态");
gamer.restoreMemento(memento);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println();
}
}
}
结果
要点
- 为了实现两套接口,可以利用 Java 的可见性
- 可以使用多个 Memento
- 注意 Memento 的有效期