背景
我们在使用文本编辑器时,如果不小心删除了某一句话,可以通过撤销功能将文件恢复至之前的状态。而 Memento 是这样的一种设计模式,他事先将某个时间点的实例保存下来,之后有必要时,再将实例恢复至当时的状态。
Memento 实现的功能
- Undo(撤销)
- Redo(重做)
- History(历史记录)
- Snapshot(快照)
登场角色
Originator 生成者
Originator 角色会在保存自己的最新状态时生成 Memento 角色;把以前保存的 Memento 角色传递给 Originator 角色时,他将恢复到生成至该 Memeto 角色时的状态
Memento 纪念品
Memento 角色会将 Originator 角色的内部信息整合在一起,对于Memento 里面的信息,有两种获取的方法:
- wide interface —— 宽接口
宽接口:用于获取恢复对象状态信息的方法集合,会暴露 Memento 对象里面所有信息,使用宽接口的仅限于 Originator 角色
- narrow interface —— 窄接口
窄接口:为外部的Caretaker 角色 提供了窄接口,获取到的信息会十分有限,防止信息泄露
宽接口和窄接口有效地保护了对象的封装性,实现宽接口与窄接口其实依靠的是Java语言的可见性,宽接口使用 默认修饰符,窄接口使用 public 修饰
Caretaker 负责人
当 Caretaker 角色想要保存当前 Orignator 角色的状态时,会通知 Orignator 角色, Orignator 角色接收通知后会返回 Memento 角色。Caretaker角色会一直保存Memento实例,便于以后通过 Orignator 角色恢复实例
Caretaker 角色只能使用Memento 中的窄接口,它只是把 Originator 角色生成的Memento 角色 当做成一个黑盒
类图
示例代码
示例代码实现的功能是Game随机投骰子,如果是 金钱增加,就保存当前的实例,如果是 金钱减少且减少到一定程度,就会恢复原来保存的实例。
Gamer
public class Gamer {
private int money;
private ArrayList<String> fruits;
private Random random = new Random();
private String[] fruitsName = {"西瓜", "苹果", "雪梨"};
public Gamer(int money) {
this.money = money;
this.fruits = new ArrayList<>();
}
public void bet() {
int dict = random.nextInt(6) + 1;
if (dict == 1) {
money += 100;
System.out.println("所持金钱增加了");
} else if (dict == 2) {
money /= 2;
System.out.println("所持金钱减半了");
} else if (dict == 6) {
String f = getFruits();
System.out.println("获得了水果(" + f + ").");
} else {
System.out.println("什么都没有发生");
}
}
// 拍摄快照
public Memento createMemento() {
Memento memento = new Memento(money);
Iterator<String> it = fruits.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.startsWith("好吃的")) {
memento.addFruit(s);
}
}
return memento;
}
// 撤销
public void restoreMemento(Memento memento) {
this.money = memento.money;
this.fruits = memento.getFruits();
}
public int getMoney() {
return money;
}
private String getFruits() {
String prefix = "";
if (random.nextBoolean()) {
prefix += "好吃的";
}
return prefix + fruitsName[random.nextInt(fruitsName.length)];
}
@Override
public String toString() {
return "Gamer{" +
"money=" + money +
", fruits=" + fruits +
'}';
}
}
Memento
public class Memento {
int money;
ArrayList<String> fruits;
Memento(int money) {
this.money = money;
this.fruits = new ArrayList<>();
}
int getMoney() {
return money;
}
void addFruit(String fruitName) {
fruits.add(fruitName);
}
ArrayList<String> getFruits() {
return (ArrayList<String>) fruits.clone();
}
}
Main
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()) {
memento = gamer.createMemento();
System.out.println("所持金钱增加了,保存当前状态");
} else if (gamer.getMoney() < memento.getMoney() / 2 ) {
gamer.restoreMemento(memento);
System.out.println("所持金钱减少了,恢复到游戏前的状态");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
功能分析
- 接口的可见性
| 可见性 | 说明 |
|---|---|
| Public | 所有类可以访问 |
| Protected | 同一个包和子类可以访问 |
| 无 | 同一个包可以访问 |
| Private | 只有类自身可以访问 |
- 实例代码中只保存了一个Memento ,可以使用一个数组或者集合来保存多个 Memento