备忘录模式

备忘录模式提供了一种对象状态撤销实现机制,当系统中某一个对象需要恢复到某一历史状态时可以使用备忘录模式进行设计。

  • 发起人,也就是要备份的目标类,他提供两个方法:

    • 恢复备忘录

    • 创建备忘录

  • 备忘录,是发起人的备忘角色

  • 看护人,用于管理备忘录,比如查询备忘录、删除备忘录、保存备忘录

  • state,代表当前状态的状态字段

在业务系统中,发起人通常是业务实体,而备忘录角色通常是备份表的实体,而看护人通常就是负责写入备份的实体到备份表中。

备忘录模式的优点:

  1. 提供了一种状态恢复的实现机制,使得用户可以方便的回到一个特定的历史步骤,当新的状态无效或者存在问题的时候,可以使用暂时存储起来的备忘录将状态复原.

  2. 备忘录实现了对信息的封装,一个备忘录对象是一种发起者对象状态的表示,不会被其他代码所改动.备忘录保存了发起者的状态,采用集合来存储备忘录可以实现多次撤销的操作

备忘录模式的缺点:

  1. 资源消耗过大,如果需要保存的发起者类的成员变量比较多, 就不可避免的需要占用大量的存储空间,每保存一次对象的状态,都需要消耗一定系统资源

备忘录模式使用场景:

  1. 需要保存一个对象在某一时刻的状态时,可以使用备忘录模式.

  2. 不希望外界直接访问对象内部状态时.

Java 实现


// 发起人角色,要求进行数据备份的对象
@Data
public class Originator {
    // 当前的状态
    private String id;

    private String name;

    private String phone;

    // 在当前状态,创建一个备忘录
    public Memento createMemnto() {
        return new Memento(id, name, phone);
    }

    // 恢复备忘录的状态
    public void restoreMemento(Memento m) {
        this.id = m.getId();
        this.name = m.getName();
        this.phone = m.getPhone();
    }
}

// 备忘录对象,最好保证只有发起者可以访问该对象
@Data
class Memento {

    private String id;

    private String name;

    private String phone;

    public Memento(String id, String name, String phone) {
        this.id = id;
        this.name = name;
        this.phone = phone;
    }
}

// 负责人,用于管理一个或者多个副本的角色
@Data
class Caretaker {

    private Memento memento;

}

使用备忘录模式:

Originator o1 = new Originator();
o1.setId("1");
o1.setName("张胜男");
o1.setPhone("1843423232");
System.out.println(o1);

// 备份发起人o1
Memento memnto = o1.createMemnto();
// 将备份的信息交给Caretaker管理存储
Caretaker caretaker = new Caretaker();
caretaker.setMemento(memnto);

// 修改o1
o1.setPhone("12312321312");
System.out.println(o1);

// 恢复o1
o1.restoreMemento(caretaker.getMemento());
System.out.println(o1);

Java 投骰子游戏

设计一个收集回国和获取金钱数的投骰子游戏,游戏规则如下:

  1. 游戏玩家通过投骰子来决定下一个状态

  2. 当点数为1,玩家金钱增加

  3. 当点数为2,玩家金钱减少

  4. 当点数为6,玩家会获得水果

  5. 当金钱消耗到一定程度,就能恢复初始状态

/**
 * Memento 表示状态
 **/
public class Memento {

    int money;    //所持金钱
    ArrayList fruits; //获得的水果

    //构造函数
    Memento(int money) {
        this.money = money;
        this.fruits = new ArrayList();
    }

    //获取当前玩家所有的金钱
    int getMoney() {
        return money;
    }

    //获取当前玩家所有的水果
    List getFruits() {
        return (List)fruits.clone();
    }

    //添加水果
    void addFruit(String fruit){
        fruits.add(fruit);
    }
}

Player玩家类,只要玩家的金币还够,就会一直进行游戏,在该类中会设置一个createMemento方法,其作用是保存当前玩家状态.还会包含一个restore撤销方法,相当于复活操作:

public class Player {

    private int money;      //所持金钱
    private List<String> fruits = new ArrayList();  //获得的水果
    private Random random = new Random();   //随机数对象
    private static String[] fruitsName={    //表示水果种类的数组
      "苹果","葡萄","香蕉","橘子"
    };

    //构造方法
    public Player(int money) {
        this.money = money;
    }

    //获取当前所持有的金钱
    public int getMoney() {
        return money;
    }

    //获取一个水果
    public String getFruit() {
        String prefix = "";
        if (random.nextBoolean()) {
            prefix = "好吃的";
        }

        //从数组中获取水果
        String f = fruitsName[random.nextInt(fruitsName.length)];
        return prefix + f;
    }

    //掷骰子游戏
    public void yacht(){

        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 fruit = getFruit();
            System.out.println("获得了水果: " + fruit);
            fruits.add(fruit);
        }else{
            //骰子结果为3、4、5
            System.out.println("无效数字,继续投掷");
        }
    }

    //拍摄快照
    public Memento createMemento(){
        Memento memento = new Memento(money);
        for (String fruit : fruits) {
            if(fruit.startsWith("好吃的")){
                memento.addFruit(fruit);
            }
        }

        return memento;
    }

    //撤销方法
    public void restore(Memento memento){
        this.money = memento.money;
        this.fruits = memento.getFruits();
    }

    @Override
    public String toString() {
        return "Player{" +
                "money=" + money +
                ", fruits=" + fruits +
                '}';
    }
}

测试: 由于引入了备忘录模式,可以保存某个时间点的玩家状态,这样就可以对玩家进行复活操作.

public class MainApp {

    public static void main(String[] args) throws InterruptedException {

        Player player = new Player(100);        //最初所持的金钱数
        Memento memento = player.createMemento();       //保存最初状态

        for (int i = 0; i < 100; i++) {
            //显示扔骰子的次数
            System.out.println("=====" + i);

            //显示当前状态
            System.out.println("当前状态: " + player);

            //开启游戏
            player.yacht();
            System.out.println("所持有的金钱为: " + player.getMoney() + " 元");

            //决定如何操作Memento
            if(player.getMoney() > memento.getMoney()){
                System.out.println("赚到金币,保存当前状态,继续游戏!");
                memento = player.createMemento();
            }else if(player.getMoney() < memento.getMoney() / 2){
                System.out.println("所持金币不多了,将游戏恢复到初始状态!");
                player.restore(memento);
            }

            Thread.sleep(1000);
            System.out.println("");
        }

    }
}

最后更新于