设计模式——备忘录模式

420 阅读3分钟

1. 备忘录模式概述

备忘录模式保存一个对象的某个状态,以便在适当的时候恢复对象。

一般来说,在备忘录模式中有三个角色:

  • Memento:备忘录 —— 包含了对象的状态。
  • Originator:发起者 —— 用来根据备忘录中的记录,保存或恢复对象的状态
  • Caretaker:管理员 —— 用来增加或者获取备忘录中的记录

也就是说,创建备忘录时,先由Originator对象创建Memento对象,再由Caretaker对象保存Memento对象。而读取备忘录时,先由Caretable对象获取Memento对象,再由Originator对象读取Memento中的信息。

(1) 适用情况

在需要支持用户取消操作时,可以使用备忘录模式。

(2) 优点

可以实现状态恢复,实现回滚操作。

(3) 缺点

每次保存都会创建一个备忘录对象,消耗资源。

2. 备忘录模式实例

使用备忘录模式来实现下棋的过程,提供最基础的下棋、悔棋和重做的功能。

(1) 创建备忘录

public class Memento {
    // 记录是什么棋子
    private String label;
    // 记录横坐标
    private int x;
    // 记录纵坐标
    private int y;

    public Memento(String label, int x, int y) {
        this.label = label;
        this.x = x;
        this.y = y;
    }

    public String getLabel() {
        return label;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }
}

(2) 创建棋手类

public class ChessPlayer {
    // 记录第几步
    private int step = -1;
    // 记录是什么棋子
    private String label;
    // 记录横坐标
    private int x;
    // 记录纵坐标
    private int y;

    // 记录备忘录
    private List<Memento> mementos = new ArrayList<>();

    /**
     * 保存状态
     */
    public void save() {
        // 创建新的备忘录并增加到管理者
        mementos.add(new Memento(step, label, x, y));
    }

    /**
     * 恢复状态
     */
    public void restore() {
        Memento memento = mementos.get(this.step);
        this.label = memento.getLabel();
        this.x = memento.getX();
        this.y = memento.getY();
    }

    /**
     * 显示状态
     */
    public void show() {
        System.out.printf("棋子<%s>:当前位置:<%d, %d>%n", label, x, y);
    }

    /**
     * 下棋
     */
    public void play(String label, int x, int y) {
        // 修改棋子状态
        this.step++;
        this.label = label;
        this.x = x;
        this.y = y;
        // 记录到备忘录
        save();
        // 显示当前棋子状态
        show();
    }

    /**
     * 悔棋,状态恢复到备忘录中的前一个状态
     */
    public void undo() {
        System.out.println("*****悔棋*****");
        this.step--;
        // 获取备忘录的前一个状态
        restore();
        // 显示当前棋子状态
        show();
    }

    /**
     * 重做,状态恢复到备忘录中的后一个状态
     */
    public void redo() {
        System.out.println("*****重做*****");
        this.step++;
        // 获取备忘录的后一个状态
        restore();
        // 显示当前棋子状态
        show();
    }
}

(3) 下棋

public class MementoDemo {
    public static void main(String[] args) {
        // 创建棋手
        ChessPlayer player = new ChessPlayer();

        player.play("车", 1, 1);
        player.play("车", 1, 5);
        player.undo();
        player.redo();
    }
}

运行结果:
image.png

3. 一些思考

在我一开始学习备忘录模式时,我是有些困扰的:为什么要区分发起者和管理员呢?感觉有些多此一举。

于是在上边的例子中,我把发起者和管理员合一了,都由棋手类来完成,发现也能得到相同的结果。

后来,我思考了一下,如果把二者合一,似乎有些违背了单一职责原则。比如,对于棋手来说,他其实只关心下棋、悔棋和重做三个功能,而记录和获取备忘录不应该是它所考虑的。所以,把管理员单独作为一类,似乎是更好的选择,只不过需要更多的代码,这点就见仁见智吧。

参考引用

备忘录模式:www.runoob.com/design-patt…
备忘录模式及典型应用:blog.csdn.net/wwwdc1012/a…