设计模式(十八)备忘录模式

1、引入

  1. 浏览器回退:浏览器一般有浏览记录,当我们在一个网页上点击几次链接之后,可在左上角点击左箭头回退到上一次的页面,然后也可以点击右箭头重新回到当前页面;
  2. 数据库备份与还原:一般的数据库都支持备份与还原操作,备份即将当钱已有的数据或者记录保留,还原即将已经保留的数据恢复到对应的表中;
  3. 编辑器撤销与重做:在编辑器上编辑文字,写错时可以按快捷键Ctrl+Z撤销,撤销后可以按Ctrl+y重做;
  4. 虚拟机生成快照与恢复:虚拟机可以生成一个快照,当虚拟机发生错误时可以恢复到快照的样子;
  5. GIT版本管理:Git是最常见的版本管理软件,每提交一个新版本,实际上Git就会把他们自动串成一条时间线,每个版本都有一个版本号,使用git reset --hard 版本号,即可回到指定的版本,让代码时刻穿梭回到过去某个历史时刻;
  6. 棋牌游戏悔棋:在棋牌游戏中,有时下快了可以悔棋,回退到上一步重新下。

2、定义:在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在未来将对象恢复到原先保存的状态。它是一种对象行为型模式,其别名为Token。

3、角色分析

(1)Originator(原发器):它是一个普通类,可以创建一个备忘录,并存储它的当前内部状态,也可以使用备忘录来恢复其内部状态,一般将需要保存内部状态的类设计为原发器;

(2)Memento(备忘录):存储原发器的内部状态,根据原发器来决定保存哪些内部状态。备忘录的设计一般可以参考原发器的设计,根据实际需要确定备忘录类中的属性。需要注意的是,除了原发器本身与负责人类之外,备忘录对象不能直接供其他类使用,原发器的设计在不同的编程语言中实现机制会有所不同;

(3)Caretaker(负责人):负责人又称为管理者,它负责保存备忘录,但是不能对备忘录的内容进行操作或检查。在负责人类中可以存储一个或多个备忘录对象,它只负责存储对象,而不能修改对象,也无须知道对象的实现细节。

  备忘录模式的核心是备忘录类以及用于管理备忘录的负责人类的设计。

4、代码实例

/**
 * @author it-小林
 * @desc  棋子类 原发器角色
 * @date 2021年09月18日 15:10
 */
public class Chessman {

    private String label;

    private Integer x;

    private Integer y;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public Integer getX() {
        return x;
    }

    public void setX(Integer x) {
        this.x = x;
    }

    public Integer getY() {
        return y;
    }

    public void setY(Integer y) {
        this.y = y;
    }

    public Chessman(String label, Integer x, Integer y) {
        this.label = label;
        this.x = x;
        this.y = y;
    }

    /**
     * 保存状态
     * @return
     */
    public ChessmanMemento save(){
        return new ChessmanMemento(this.label, this.x, this.y);
    }

    /**
     * 恢复
     * @param chessmanMemento
     */
    public void restore(ChessmanMemento chessmanMemento){
        this.label = chessmanMemento.getLabel();
        this.x = chessmanMemento.getX();
        this.y = chessmanMemento.getY();
    }

    /**
     * 输出
     */
    public void show(){
        System.out.println(String.format("棋子<%s>:当前位置为:<%d, %d>", this.getLabel(), this.getX(), this.getY()));
    }
}
/**
 * @author it-小林
 * @desc   备忘录角色
 * @date 2021年09月18日 15:12
 */
public class ChessmanMemento {

    private String label;

    private Integer x;

    private Integer y;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public Integer getX() {
        return x;
    }

    public void setX(Integer x) {
        this.x = x;
    }

    public Integer getY() {
        return y;
    }

    public void setY(Integer y) {
        this.y = y;
    }

    public ChessmanMemento(String label, Integer x, Integer y) {
        this.label = label;
        this.x = x;
        this.y = y;
    }
}
/**
 * @author it-小林
 * @desc   负责人角色
 * @date 2021年09月18日 15:14
 */
public class MementoCaretaker {

    private List<ChessmanMemento> mementoList = new ArrayList<>();

    public ChessmanMemento getMemento(int i){
        return mementoList.get(i);
    }

    public void addMemento(ChessmanMemento chessmanMemento){
        mementoList.add(chessmanMemento);
    }


}
/**
 * @author it-小林
 * @desc   客户端
 * @date 2021年09月18日 15:22
 */
public class Client {

    private static int index = -1;

    private static MementoCaretaker mementoCaretaker = new MementoCaretaker();

    public static void main(String args[]){
        Chessman chessman = new Chessman("车", 1, 1);
        play(chessman);
        chessman.setX(4);
        play(chessman);
        chessman.setY(5);
        play(chessman);
        undo(chessman, index);
        undo(chessman, index);
        redo(chessman, index);
        redo(chessman, index);

    }

    /**
     * 下棋,同时保存备忘录
     * @param chessman
     */
    public static void play(Chessman chessman){
        mementoCaretaker.addMemento(chessman.save());
        index++;
        chessman.show();
    }

    /**
     * 悔棋,撤销到上一个备忘录
     * @param chessman
     * @param i
     */
    public static void undo(Chessman chessman, int i){
        System.out.println("*******悔棋*********");
        index--;
        chessman.restore(mementoCaretaker.getMemento(i-1));
        chessman.show();
    }

    /**
     * 撤销悔棋,恢复到下一个备忘录
     * @param chessman
     * @param i
     */
    public static void redo(Chessman chessman, int i){
        System.out.println("*****撤销悔棋*****");
        index++;
        chessman.restore(mementoCaretaker.getMemento(i + 1));
        chessman.show();
    }
}

5、优缺点

(1)优点

  • 它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原;
  • 备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。

(2)缺点

  • 资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。

6、使用场景

  • 保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时它能够恢复到先前的状态,实现撤销操作;

  • 防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。
如本文有侵权行为,请及时与本人联系,多多包涵! 小生初出茅庐,多多指教!

本文来自博客园,作者:it-小林,转载请注明原文链接:https://www.cnblogs.com/linruitao/p/15067863.html

原文地址:https://www.cnblogs.com/linruitao/p/15067863.html