【Java设计模式】备忘录模式

为了使软件的使用更加人性化,对于误操作,我们需要提供一种类似“后悔药”的机制,让软件系统可以回到误操作前的状态,因此需要保存用户每一次操作时系统的状态,一旦出现误操作,可以把存储的历史状态取出即可回到之前的状态

现在大多数软件都有撤销(Undo)的功能,快捷键一般都是ctrl+Z,目的就是为了解决这个后悔的问题。

? 备忘录的模式动机很简单

在应用软件的开发过程中,很多时候我们都需要记录一个对象的内部状态。

在具体实现过程中,为了允许用户取消不确定的操作或从错误中恢复过来,需要实现备份点和撤销机制,而要实现这些机制,必须事先将状态信息保存在某处,这样才能将对象恢复到它们原先的状态。

备忘录模式是一种给我们的软件提供后悔药的机制,通过它可以使系统恢复到某一特定的历史状态。

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

备忘录模式包含如下角色:

  • Originator:原发: 记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
  • Memento:备忘录 :负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
  • Caretaker:负责人: 对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。

在这里插入图片描述

? 下面我们演示一下:(简化了,突出中心)
博文类

public class Article {

    private String title;
    private String content;
    private String imgs;

    public Article(String title, String content, String imgs) {
        this.title = title;
        this.content = content;
        this.imgs = imgs;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getImgs() {
        return imgs;
    }

    public void setImgs(String imgs) {
        this.imgs = imgs;
    }

    public ArticleMemento saveToMemento() {
        ArticleMemento articleMemento = new ArticleMemento(this.title,this.content,this.imgs);
        return articleMemento;
    }

    public void undoFromMemento(ArticleMemento articleMemento) {

        this.title = articleMemento.getTitle();
        this.content = articleMemento.getContent();
        this.imgs = articleMemento.getImgs();
    }

    @Override
    public String toString() {
        return "Article{" +
                "title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", imgs='" + imgs + '\'' +
                '}';
    }
}

博文备忘录类(相当于快照)

public class ArticleMemento {
    private String title;
    private String content;
    private String imgs;

    public ArticleMemento(String title, String content, String imgs) {
        this.title = title;
        this.content = content;
        this.imgs = imgs;
    }

    public String getTitle() {
        return title;
    }

    public String getContent() {
        return content;
    }

    public String getImgs() {
        return imgs;
    }

    @Override
    public String toString() {
        return "ArticleMemento{" +
                "title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", imgs='" + imgs + '\'' +
                '}';
    }
}

博文备忘录管理类

import java.util.Stack;

public class ArticleMementoManager {

    private final Stack<ArticleMemento> ARTICLE_MEMENTO_STACK = new Stack<ArticleMemento>();

    public ArticleMemento getMemento()
    {
        ArticleMemento articleMemento= ARTICLE_MEMENTO_STACK.pop();
        return articleMemento;
    }

    public void addMemento(ArticleMemento articleMemento)
    {
        ARTICLE_MEMENTO_STACK.push(articleMemento);
    }

}

测试类

public class Test {

    public static void main(String[] args) {
        ArticleMementoManager articleMementoManager = new ArticleMementoManager();

        Article article= new Article("沉晓的设计模式A","博文内容A","博文图片A");

        ArticleMemento articleMemento = article.saveToMemento();

        articleMementoManager.addMemento(articleMemento);
        System.out.println("标题:"+article.getTitle()+" 内容:"+article.getContent()+" 图片:"+article.getImgs()+" 暂存成功");

        System.out.println("博文完整信息:"+article);


        System.out.println("修改博文start");

        article.setTitle("沉晓的设计模式B");
        article.setContent("博文内容B");
        article.setImgs("博文图片B");

        System.out.println("修改博文end");

        System.out.println("博文完整信息:"+article);

        articleMemento = article.saveToMemento();
        articleMementoManager.addMemento(articleMemento);


        article.setTitle("沉晓的设计模式C");
        article.setContent("手记内容C");
        article.setImgs("手记图片C");

        System.out.println("暂存回退start");

        System.out.println("回退出栈1次");
        articleMemento = articleMementoManager.getMemento();
        article.undoFromMemento(articleMemento);

        System.out.println("回退出栈2次");
        articleMemento = articleMementoManager.getMemento();
        article.undoFromMemento(articleMemento);


        System.out.println("暂存回退end");
        System.out.println("手记完整信息:"+article);

    }
}

结果:
在这里插入图片描述
类图:
在这里插入图片描述

? 模式分析

  • 由于在备忘录中存储的是发起人的中间状态,因此需要防止原发器以外的其他对象访问备忘录。
  • 备忘录对象通常封装了原发器的部分或所有的状态信息,而且这些状态不能被其他对象访问,也就是说不能在备忘录对象之外保存原发器状态,因为暴露其内部状态将违反封装的原则,可能有损系统的可靠性和可扩展性。

为了实现对备忘录对象的封装,需要对备忘录的调用进行控制

  • 对于原发器而言,它可以调用备忘录的所有信息,允许原发器访问返回到先前状态所需的所有数据;
  • 对于负责人而言,只负责备忘录的保存并将备忘录传递给其他对象;
  • 对于其他对象而言,只需要从负责人处取出备忘录对象并将原发器对象的状态恢复,而无须关心备忘录的保存细节。

理想的情况是只允许生成该备忘录的那个原发器访问备忘录的内部状态。

? 模式优缺点

备忘录模式的优点

  • 提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用先前存储起来的备忘录将状态复原。
  • 实现了信息的封装,一个备忘录对象是一种原发器对象的表示不会被其他代码改动,这种模式简化了原发器对象,备忘录只保存原发器的状态,采用堆栈来存储备忘录对象可以实现多次撤销操作,可以通过在负责人中定义集合对象来存储多个备忘录

备忘录模式的缺点

  • 资源消耗过大,如果类的成员变量太多,就不可避免占用大量的内存,而且每保存一次对象的状态都需要消耗内存资源,如果知道这一点大家就容易理解为什么一些提供了撤销功能的软件在运行时所需的内存和硬盘空间比较大了。

? 在以下情况下可以使用备忘录模式

  • 保存一个对象在某一个时刻的状态或部分状态,这样以后需要时它能够恢复到先前的状态。
  • 如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过负责人可以间接访问其内部状态。

? 模式应用
(1)几乎所有的文字或者图像编辑软件都提供了撤销(ctrl+z)的功能,即撤销操作,但是当软件关闭再打开时不能再进行撤销操作,也就是说不能再回到关闭软件前的状态,实际上这中间就使用到了备忘录模式,在编辑文件的同时可以保存一些内部态,这些状态在软件关闭时从内存销毁,当然这些状态的保存也不是无限的,很多软件只提供有限次的撤销操作。
(2)数据库管理系统DBMS所提供的事务管理应用了备忘录模式,当数据库某事务中一条数据操作语句执行失败时,整个事务将进行回滚操作,系统回到事务执行之前的状态

? 模式拓展

备忘录的封装性

  • 为了确保备忘录的封装性,除了原发器外,其他类是不能也不应该访问备忘录类的,在实际开发中,原发器与备忘录之间的关系是非常特殊的,它们要分享信息而不让其他类知道,实现的方法因编程语言的不同而不同。
  • C++可以用 friend关键字,使原发器类和备忘录类成为友元类,互相之间可以访问对象的一些私有的属性;
  • 在Java语言中可以将两个类放在一个包中,使它们之间满足默认的包内可见性,也可以将备忘录类作为原发器类的内部类,使得只有原发器才可以访问备忘录中的数据,其他对象都无法使用备忘录中的数据。

多备份实现

  • 在负责人中定义一个集合对象来存储多个状态,而且可以方便地返回到某一历史状态。
  • 在备份对象时可以做一些记号,这些记号称为检查点( Check point)。在使用 Hash Map等实现时可以使用Key来设置检查点。
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值