【设计模式】备忘录模式不只是记录怕忘记的东西那么简单!

456 阅读3分钟

👉设计模式目录

什么是备忘录模式(Memento)

概念

备忘录(Memento)模式属于行为型模式,又叫作快照模式,定义:在不破坏封装性的前提下,捕获一个对象内部的状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。简单来说,就是保存一个对象之前的状态,然后在某个时间回退到这个状态

我们使用的很多软件都提供了这个功能,我们使用的编辑工具,word、记事本、idea、typora等等,ctrl+z就可以回退自己的操作,可以防止我们的误操作。还有浏览器的倒退,可以让我们回到上一个页面。游戏中的存档,我们可以自己提前存入一个档,以后可以再次退回来。

ctrl+z

优点

  1. 给用户提供了一种可以恢复状态的机制,用户可以回退自己的操作。
  2. 封装了内部状态。想要使用备忘录模式,需要把内部的数据都封装成一个状态。
  3. 符合单一职责原则。由发起类无需管理状态,由管理类来管理状态。

缺点

  1. 需要大量的资源。要保存状态对象是需要消耗内存资源,可以考虑设置一个状态对象的上限。

原则

“+”代表遵守,“-”代表不遵守或者不相关

原则开放封闭单一职责迪米特里氏替换依赖倒置接口隔离合成复用
-++----

适用场景

  1. 需要保存和恢复数据的情况。
  2. 需要撤销操作的情况。

这两种常见的代码实现会有一点点的不同,后面的例子中我会标注一下的。

如何实现

想要实现备忘录模式,需要以下三样东西:

  1. 发起类(Originator):依赖备忘录对象,创建备忘录对象,提供创建备忘录和恢复备忘录,发起类可以访问备忘录中的信息。
  2. 备忘录类(Memento):记录发起类当前时刻的状态,并且保存在管理类中。
  3. 管理类(Caretaker):管理备忘录对象,提供存储,回退备忘录等操作,管理类不能访问备忘录对象内的信息。

类图

备忘录模式的结构图

上班小丑

开始敲代码!

举例

这里以记事本的回退操作为例,我们输入文字,每个版本的文字都保存起来,我们可以一个一个版本地回退。

类图

image-20210612225959096

代码

发起类

/**
 * 发起类
 * 文本编辑器
 * Created on 2021/6/12.
 *
 * @author xuxiaobai
 */
public class TextEditor {
    //当前状态记录
    private TextMemento currentRecord;

    //状态记录管理者
    private TextCaretaker caretaker = new TextCaretaker();

    /**
     * 提交文本记录
     *
     * @param text
     */
    public void commit(String text) {
        System.out.println("-----commit-----");
        //更新当前记录
        currentRecord = new TextMemento(text);
        //保存记录
        caretaker.push(currentRecord);
        show();
    }

    /**
     * 回退
     */
    public void ctrlZ() {
        System.out.println("-----ctrl+z-----");
        TextMemento textMemento = caretaker.get();
        if (textMemento == null) {
            //当备忘录为空,打印提示后返回
            System.out.println("没有历史记录,无法回退");
            return;
        }
        System.out.println("恢复记录");
        this.currentRecord=textMemento;
        show();
    }

    /**
     * 展示当前记录
     */
    public void show() {
        //获取备忘录中的text
        String text = currentRecord.getText();
        System.out.println("当前记录为:“"+ (text ==null?"无": text)+"”");
    }


}

管理类

/**
 *
 * 文本备忘录管理类
 * Created on 2021/6/12.
 *
 * @author xuxiaobai
 */
public class TextCaretaker {
    /**
     * 备忘录存储栈
     * 这是需要撤销操作的情况,才需要的
     * 如果是需要保存和恢复数据的情况,可以考虑使用List来存储备忘录
     */
    Stack<TextMemento> stack=new Stack<>();


    public void push(TextMemento memento){
        stack.push(memento);
    }

    public TextMemento get(){
        return stack.pop();
    }

}

备忘录类

/**
 * 文本备忘录
 * Created on 2021/6/12.
 *
 * @author xuxiaobai
 */
public class TextMemento {

    /**
     * 文本的属性
     * 这里以String为例
     * 如果你想更加复杂一点,可以增加多一点的属性
     */
    private String text;


    public TextMemento(String text){
        this.text =text;
    }


    public String getText() {
        return text;
    }

    public TextMemento setText(String text) {
        this.text = text;
        return this;
    }
}

测试类

public class MementoTest {
    public static void main(String[] args) {
        //文本编辑器
        TextEditor editor=new TextEditor();
        StringBuilder text=new StringBuilder("第一个版本的文字");
        editor.commit(text.toString());
        editor.commit(text.append(",再加第二版本").toString());
        editor.commit(text.append(",再加第三版本").toString());
        editor.show();
        editor.ctrlZ();
        editor.ctrlZ();
        editor.ctrlZ();
        /**
         * 结果:
         * -----commit-----
         * 当前记录为:“第一个版本的文字”
         * -----commit-----
         * 当前记录为:“第一个版本的文字,再加第二版本”
         * -----commit-----
         * 当前记录为:“第一个版本的文字,再加第二版本,再加第三版本”
         * 当前记录为:“第一个版本的文字,再加第二版本,再加第三版本”
         * -----ctrl+z-----
         * 恢复记录
         * 当前记录为:“第一个版本的文字,再加第二版本,再加第三版本”
         * -----ctrl+z-----
         * 恢复记录
         * 当前记录为:“第一个版本的文字,再加第二版本”
         * -----ctrl+z-----
         * 恢复记录
         * 当前记录为:“第一个版本的文字”
         */
    }
}

这里的例子我都是直接创建对象的,偶尔偷个懒嘛,我这里建议在实际开发中运用的话,还是要去创建一个顶层的接口,然后声明的时候依赖于接口,这样才符合依赖倒转原则嘛。

小黄鸡扯紫色内裤动图

总结

备忘录模式和其他模式相比有很大的不同,它是为了能让我们回退我们的操作,保存了过往的状态,我们在合适的时间恢复。在使用备忘录模式时需要注意,只有发起类这个角色才能去组合备忘录和管理者,不要把这两个角色暴露给这个模式外的对象,也只有发起类才能去读取备忘录中的数据,管理者只负责管理备忘录,还有呢,使用备忘录模式是需要消耗内存的,也可以考虑把这些备忘录写入到数据库中,不过使用的时候还是得写入内存中。

别打扰我玩最爱的摩尔庄园

玩摩尔庄园去了。

——————————————————————————————

你知道的越多,不知道的就越多。

如果本文章内容有问题,请直接评论或者私信我。如果觉得我写得还不错的话,点个赞也是对我的支持哦

未经允许,不得转载!