设计模式-备忘录模式

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

先来看下类图

该模式涉及到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());

    }

}

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

*/

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容