备忘录模式
我们在使用文本编辑器编写文件时,如果不小心删除了某句话,可以通过撤销(undo )功能将文件恢复至之前的状态。有些文本编辑器甚至支持多次撤销,能够恢复至很久之前的版本。
使用面向对象编程的方式实现撤销功能时,需要事先保存实例的相关状态信息。然后,在撤销时,还需要根据所保存的信息将实例恢复至原来的状态。
事例程序
- 游戏是自动进行的
- 游戏的主人公通过掷骰子来决定下一个状态
- 当骰子点数为1的时候,主人公的金钱会增加
- 当骰子点数为2的时候,主人公的金钱会减少
- 当骰子点数为6的时候,主人公会得到水果
- 主人公没有钱时游戏就会结束
在程序中,如果金钱增加,为了方便将来恢复状态,我们会生成Memento类的实例,将现在的状态保存起来。所保存的数据为当前持有的金钱和水果。如果不断掷出了会导致金钱减少的点数,为了防止金钱变为0而结束游戏,我们会使用Memento的实例将游戏恢复至之前的状态。
类的一览表
名字 | 说明 |
Memento | game的状态类 |
Gamer | 游戏的主人公 |
Main | 测试 |
类图
代码
Memento
- money表示主人公现在所持有的金钱数目,fruits表示现在为止所获得的水果。
public class Memento { int money;//所持金钱 ArrayList<String> fruits;//所获得水果 public int getMoney() { return money; } Memento(int money) { this.money = money; this.fruits = new ArrayList<>(); } //添加水果 void addFruit(String fruit) { fruits.add(fruit); } //获取所有水果 List getFruit() { return (List) fruits.clone(); } }
Gamer
Gamer类是表示游戏主人公的类。它有3个子段,即所持金钱(money)获得的水果(fruits )以及一个随机数生成器(random)。而且还有一个名为fruitsname 的静态字段。
- bet:,只要主人公没有破产,就会一直掷骰子,并根据骰子结果改变所持有的金钱数目和水果个数。
- createMemento方法的作用是保存当前的状态(拍摄快照)。在createMemento方法中,会根据当前在时间点所持有的金钱和水果生成一个Memento类的实例,该实例代表了“当前Gamer的状态”,它会被返回给调用者。(只保存好吃的水果)
- restoreMemento方法的功能与createMemento相反,它会根据接收到的Memento类的实例来将Gamer恢复为以前的状态
public class Gamer { private int money; private List<String> fruits = new ArrayList<>(); private Random random = new Random(); private static String[] fruitName = {"苹果", "葡萄", "香蕉", "橘子"}; 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); fruits.add(f); } else { //3,4,5无事发生 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.addFruit(f); } } return m; } //撤销 public void restoreMemento(Memento memento) { this.money = memento.money; this.fruits = memento.getFruit(); } @Override public String toString() { return "money = " + money + " fruits = " + fruits; } //获得水果 private String getFruit() { String prefix = ""; if (random.nextBoolean()) { prefix = "好吃的"; } return prefix + fruitName[random.nextInt(fruitName.length)]; } }
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()) { 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) { e.printStackTrace(); } } } }
时序图