设计模式-备忘录模式

备忘录模式是使用一个备忘录对象把另外一个对象内部状态进行保存,在适当的时候还原到某个状态。如同我们记录某件事件,在需要回忆的时候去看下记事本。

先来看下类图

该模式涉及到3个角色:

  • 发起人角色:Originator,该角色包含备忘录对象,备忘录对象存储了他的状态;
  • 负责人角色:Caretaker,该角色保存备忘录对象,但不检查备忘录对象内容;
  • 备忘录角色:Memento,将发起人对象的状态保存起来,,保护发起人的内容不被外界访问

宽接口与白箱

备忘录角色对如何其他对象提供一个接口,也就是宽接口的话,那么备忘录角色存储的内部状态都暴露给其他对象。这种情况导致发起人的状态都没看到,是破坏封装性的,只能通过程序猿的自律。先来看下宽接口。

接下来看下代码实现:

public class Memento {    private String state;    public Memento(String state) {        this.state=state;
    }    public String getState() {        return this.state;
    }    public void setState(String state){        this.state=state;
    }

}

上面这个备忘录对象提供:1、对发起人的状态进行保存;2、可以访问的状态是公开的;

//发起人public class Originator {    private String state;    //创建备忘录对象来保存状态
    public Memento createMemento(){        return new Memento(state);
    }    //从备忘录对象里面恢复状态
    public void restoreMemento(Memento m){        this.state=m.getState();
    }    public String getState() {        return state;
    }    public void setState(String state) {
        System.out.println("当前状态:"+state);        this.state = state;
    }
}

发起人对象利用创建一个新的备忘录对象保存自己的状态;

//负责人public class Caretaker {    private Memento memento;    public Memento retrieveMemento(){        return this.memento;
    }    public void saveMemento(Memento m){        this.memento=m;
    }

}

负责人只负责保存备忘录对象,对备忘录对象里面的内容不再变化。客户端的操作如下

public class Client {    public static void main(String[] args) {
        Originator o=new Originator();
        Caretaker c=new Caretaker();        //发起人状态改变
        o.setState("Start");        //负责人保存这个备忘录
        c.saveMemento(o.createMemento());        //改变状态
        o.setState("End");        //回到初始
        o.restoreMemento(c.retrieveMemento());

    }

}/**  ---- result ----

当前状态:Start
当前状态:End
*/

上面就是白箱操作,很简单,但是破坏了对发起人的状态封装;

双重接口

所谓双重接口就是对某一个对象提供宽接口,对另外一些类提供窄接口。系统中可能需要将某个对象的状态保存起来,在某个时候进行恢复,但这些状态并不希望被外界访问,以免有外界直接修改状态的危险,这个时候,备忘录模式就很好的解决这个问题,他利用宽接口和窄接口来保证。

假设窄接口对所有类公开,而公开类只对某一个公开,这个时候,我们可以把实现了宽接口和窄接口的具体类作为这个特殊类的内部类,宽接口的方法也可以移植到这个特殊类上,而具体类里面的方法都是私有,这样对特殊类可以访问所有接口,其他类智能调用特殊类的某些公开方法。有点饶人,画个图

图1是一个基本样子,进行演变,首先宽接口方法归属到具体类里面,变成下面这个样子

这样宽接口的方法还是公开的,此时把具体类作为特殊类的内部类,并且,把里面的方法都设置私有

这个时候的特殊类,也就发起者代码如下:

public class DoubleInterfaceDemo {    public static void main(String[] args) {
        DoubleInterfaceDemo demo=new DoubleInterfaceDemo();
        demo.new ConcreteCLass().wide();
        demo.new ConcreteCLass().getConcrete().does();
    }    class ConcreteCLass implements Narrow{        @Override
        public void does() {
            System.out.println("窄接口");
        }        //这个接口只能DoubleInterfaceDemo自己用了
        private void wide(){
            System.out.println("宽接口");
        }        public Narrow getConcrete(){            return (Narrow)new ConcreteCLass();
        }

    }
}

接下来看下黑箱的备忘录模式,有了上面的介绍,那我们把备忘录的具体实现作为内部类放到发起人对象里面

下面看下具体代码

//发起人 加强版 黑箱public class Originator2 {    private String state;    public Originator2() {

    }    public MementoIF createMemento(){        return new Memento2(this.state);
    }    //内部类 备忘录
    protected class Memento2 implements MementoIF{        private String saveState;        public Memento2(String saveState) {            this.saveState=saveState;
        }        public String getSaveState() {            return saveState;
        }        public void setSaveState(String saveState) {            this.saveState = saveState;
        }
    }    //从备忘录对象里面恢复状态
    public void restoreMemento(MementoIF m){        this.state=((Memento2)m).getSaveState();
    }    public String getState() {        return state;
    }    public void setState(String state) {
        System.out.println("当前状态:"+state);        this.state = state;
    }
}

负责人针对接口编程,代码如下

//负责人public class Caretaker2 {    private MementoIF memento;    public MementoIF retrieveMemento(){        return this.memento;
    }    public void saveMemento(MementoIF m){        this.memento=m;
    }

}

MementoIF这个接口是窄接口,里面可以有公共使用的方法,这里假设没有方法。 最后测试下这个代码

public class Client2 {    public static void main(String[] args) {
        Originator2 o=new Originator2();
        Caretaker2 c=new Caretaker2();        //发起人状态改变
        o.setState("Start");        //负责人保存这个备忘录
        c.saveMemento(o.createMemento());        //改变状态
        o.setState("End");        //回到初始
        o.restoreMemento(c.retrieveMemento());

    }

}/**
最后结论和之前的一样,但在设计上面已经很不同

*/

有时候发起人内部信息需要保存在别的地方,但是读取还是发起人自己,此时备忘录模式就可以把发起人信息对外封闭起来。

原文发布于微信公众号 - 技术与生活(technology_life)

原文发表时间:2016-11-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏一枝花算不算浪漫

Matcher类的简单使用

2917
来自专栏源码之家

word如何自动分割成多个文档

3875
来自专栏我杨某人的青春满是悔恨

封装一个 Swift-Style 的网络模块

Swift 跟 OC 有着完全不同的设计哲学,它鼓励你使用 protocol 而不是 super class,使用 enum 和 struct 而不是 clas...

913
来自专栏人工智能LeadAI

资源安全

在所有异常分之里记得释放资源,存在较为严重的安全隐患。此处,引入ScopedExit的封装,使用C++特有的RAII机制,在析构函数中完成资源的安全释放;即使程...

862
来自专栏nimomeng的自我进阶

Collection官方文档

a) Keys必须实现NSCopying协议。添加成员的方法并不将每一个key直接进行添加,而是将每一个key进行copy并将copy后对象添加...

1494
来自专栏java学习

Java每日一练(2017/6/8)

Java基础 | 数据库 | Android | 学习视频 | 学习资料下载 课前导读 ●回复"每日一练"获取以前的题目! ●答案公布时间:为每期发布题目的第二...

2717
来自专栏非典型技术宅

Swift实践:使用CoreData存储多种数据类的通讯录1. CoreData支持存储数据类型2. 使用CoreData存储多种数据类的通讯录3. Codable

1863
来自专栏Java成神之路

Java微信开发_Exception_01_The type org.xmlpull.v1.XmlPullParser cannot be resolved. It is indirectly ref

这个异常是在做微信开发时出现的,在引入了XStream的jar包之后,还是出现了如下错误信息:

913
来自专栏岑玉海

Spark源码系列(九)Spark SQL初体验之解析过程详解

好久没更新博客了,之前学了一些R语言和机器学习的内容,做了一些笔记,之后也会放到博客上面来给大家共享。一个月前就打算更新Spark Sql的内容了,因为一些别的...

4175
来自专栏IT开发技术与工作效率

VBA导入

1645

扫码关注云+社区

领取腾讯云代金券