模式介绍

  • 备忘录模式(Memento Pattern)在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
  • 可以这里理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情,或者是记录已经达成的共同意见的事情,以防忘记了。在软件层面,备忘录模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作。
  • 备忘录模式属于行为型模式

UML类图

image.png

类图解析:

  • Originator(发起人):负责创建一个备忘录Memento,用以记录当前时刻自身的内部状态,并可使用备忘录恢复内部状态。Originator可以根据需要决定Memento存储自己的哪些内部状态。
  • Memento(备忘录):负责存储Originator对象的内部状态,并可以防止Originator以外的其他对象访问备忘录。备忘录有两个接口:Caretaker只能看到备忘录的窄接口,他只能将备忘录传递给其他对象。Originator却可看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。
  • Caretaker(管理者):负责备忘录Memento,不能对Memento的内容进行访问或者操作。

备忘录模式案例

背景介绍: 以上面的UML为例,设计一个小游戏存档恢复系统

Originator类

public class Originator {
    private String state;

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    /**
     * 保存状态
     */
    public Memento save() {
        return new Memento(getState());
    }

    /**
     * 恢复状态
     */
    public void resume(Memento memento) {
        state = memento.getState();
    }
}

Memento类

public class Memento {
    private String state;

    public Memento(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
}

Caretaker类

public class Caretaker {
    private List<Memento> mementos = new ArrayList<>();

    /**
     * 添加状态
     */
    public void addState(Memento memento) {
        System.out.println("添加存档:" + memento.getState());
        mementos.add(memento);
    }

    /**
     * 获取状态
     */
    public Memento getState(int index) {
        Memento memento = mementos.get(index);
        System.out.println("恢复存档:" + memento.getState());
        return memento;
    }
}

Client测试类

public class Client {
    public static void main(String[] args) {
        // 游戏开始
        Originator originator = new Originator();

        // 游戏存档
        Caretaker caretaker = new Caretaker();

        // 游戏进度
        originator.setState("剩余100点血");
        System.out.println("++通过第一关,前往第二关++");
        caretaker.addState(originator.save());
        originator.setState("剩余75点血");
        System.out.println("++通过第二关,前往第三关++");
        caretaker.addState(originator.save());
        originator.setState("战败~~");
        caretaker.addState(originator.save());
        System.out.println("++请选择恢复存档店++");
        System.out.println("++1.第二关开始100点血 2.第三关开始75点血++");
        System.out.print("请选择:");
        Scanner scanner = new Scanner(System.in);
        switch (scanner.next()) {
            case "1":
                System.out.println("恢复第二关开始存档点");
                originator.resume(caretaker.getState(0));
                break;
            case "2":
                System.out.println("恢复第二关开始存档点");
                originator.resume(caretaker.getState(1));
                break;
            default:
                System.out.println("退出~");
                break;
        }
    }
}

测试结果:

image.png

备忘录模式优缺点

优点

  • 保存与被保存状态处于关键对象的外面,有助于维护内聚
  • 保存关键对象的数据封装。
  • 提供容易实现的恢复能力

缺点

  • 备忘与用于保存状态。
  • 使用备忘录,保存和恢复状态可能相当耗时
  • 在Java系统中考虑使用序列化(Serialization 即单例模式)来保存对象的状态