您当前的位置: 首页 > 

Dongguo丶

暂无认证

  • 2浏览

    0关注

    472博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

设计模式之备忘录模式

Dongguo丶 发布时间:2019-01-14 00:05:28 ,浏览量:2

模式定义

​ 所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。

​备忘录模式将要保存的细节给封装在备忘录中,就是那天要改变保存的细节也不会影响到客户端。

 模式结构

​ 下图是备忘录模式的UML结构图:

 

​ 备忘录模式主要包含入下几个角色:

​ Originator: 原发器。负责创建一个备忘录,用以记录当前对象的内部状态,通过也可以使用它来利用备忘录恢复内部状态。同时原发器还可以根据需要决定Memento存储Originator的那些内部状态。

​ Memento: 备忘录。用于存储Originator的内部状态,并且可以防止Originator以外的对象访问Memento。在备忘录Memento中有两个接口,其中Caretaker只能看到备忘录中的窄接口,它只能将备忘录传递给其他对象。Originator可以看到宽接口,允许它访问返回到先前状态的所有数据。

​ Caretaker: 负责人。负责保存好备忘录,不能对备忘录的内容进行操作和访问,只能够将备忘录传递给其他对象。

​在备忘录模式中,最重要的就是备忘录Memento了。我们都是备忘录中存储的就是原发器的部分或者所有的状态信息,而这些状态信息是不能够被其他对象所访问了,也就是说我们是不可能在备忘录之外的对象来存储这些状态信息,如果暴漏了内部状态信息就违反了封装的原则,故备忘录是除了原发器外其他对象都是不可以访问的。

​ 所以为了实现备忘录模式的封装,我们需要对备忘录的访问做些控制:

​ 对原发器:可以访问备忘录里的所有信息。

​ 对负责人:不可以访问备忘录里面的数据,但是他可以保存备忘录并且可以将备忘录传递给其他对象。

​ 其他对象:不可访问也不可以保存,它只负责接收从负责人那里传递过来的备忘录同时恢复原发器的状态。

​ 所以就备忘录模式而言理想的情况就是只允许生成该备忘录的那个原发器访问备忘录的内部状态。

​ 典型的备忘录代码如下:


class Memento {
    private String state;
    public Memento(Originator o){
      state = o.state;
    }
    public void setState(String state){
       this.state=state;
    }
    public String getState(){
       return this.state;
    }  
}
分类:

1.”白箱”备忘录模式的实现 这里写图片描述 2.“黑箱”备忘录模式的实现 这里写图片描述  3.“多重”检查点 这里写图片描述 4.”自述历史”模式 这里写图片描述

下面对三种在Java中可保存封装的方法进行探讨。   第一种就是采用两个不同的接口类来限制访问权限。这两个接口类中,一个提供比较完备的操作状态的方法,我们称它为宽接口;而另一个则可以只是一个标示,我们称它为窄接口。备忘录角色要实现这两个接口类。这样对于“备忘发起角色”采用宽接口进行访问,而对于其他的角色或者对象则采用窄接口进行访问。

  这种实现比较简单,但是需要人为的进行规范约束——而这往往是没有力度的。

  第二种方法便很好的解决了第一种的缺陷:采用内部类来控制访问权限。将备忘录角色作为“备忘发起角色”的一个私有内部类。好处我不详细解释了,看看代码吧就明白了。下面的代码是一个完整的备忘录模式的教学程序。它便采用了第二种方法来实现备忘录模式。

  还有一点值得指出的是,在下面的代码中,对于客户程序来说“备忘录管理者角色”是不可见的,这样简化了客户程序使用备忘录模式的难度。下面采用“备忘发起角色”来调用访问“备忘录管理者角色”,也可以参考门面模式在客户程序与备忘录角色之间添加一个门面角色。

第三种方式是不太推荐使用的:使用clone方法来简化备忘录模式。由于Java提供了clone机制,这使得复制一个对象变得轻松起来。使用了clone机制的备忘录模式,备忘录角色基本可以省略了,而且可以很好的保持对象的封装。但是在为你的类实现clone方法时要慎重啊。

  在上面的教学代码中,我们简单的模拟了备忘录模式的整个流程。在实际应用中,我们往往需要保存大量“备忘发起角色”的历史状态。这时就要对我们的“备忘录管理者角色”进行改造,最简单的方式就是采用容器来按照顺序存放备忘录角色。这样就可以很好的实现undo、redo功能了。

“白箱”备忘录模式的实现 备忘录角色对任何对象都提供一个接口,即宽接口,备忘录角色的内部所存储的状态就对所有对象公开。因此这个实现又叫做“白箱实现”。    “白箱”实现将发起人角色的状态存储在一个大家都看得到的地方,因此是破坏封装性的。但是通过程序员自律,同样可以在一定程度上实现模式的大部分用意。因此白箱实现仍然是有意义的。    下面给出一个示意性的“白箱实现”。

* 类似白箱备忘录模式
 *
 */
public class Client {

    /**
     * 客户端
     * 
     * @param args
     */
    public static void main(String[] args) {

        int state = 3;
        Originator originator = new Originator();
        Caretaker caretaker = new Caretaker();

        originator.setState(state);
        /**
         * 创建备忘录对象的 缓存起来
         */
        caretaker.saveMemento(originator.creatMementoObject());
        /*
         * 进行设置重新还原
         */
        originator.setState(5);
        System.out.println("发起人更改状态:" + originator.getState());

        originator.restoreMemento(caretaker.retrieveMemento());

    }

}

管理者,负责发起者的管理


/**
 * 管理者 负责管理Caretaker
 * 
 * 
 */
public class Caretaker {

    private Memento memento;

    /**
     * 备忘录的取值方法
     */
    public Memento retrieveMemento() {
        return this.memento;
    }

    /**
     * 备忘录的赋值方法
     */
    public void saveMemento(Memento memento) {
        this.memento = memento;
    }

}

备忘录,对发起者进行缓存的类

/**
 * 备忘录
 * 
 */
public class Memento {

    private int state;

    public Memento() {
        super();
    }

    public Memento(int state) {
        this.state = state;
        System.out.println("备忘录 当前保存 状态:" + state);
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

}

发起者,负责备忘录的创建,修改,恢复

/**
 * 发起者
 * 
 */
public class Originator {

    private int state = 0;

    Caretaker caretaker = new Caretaker();

    public Memento creatMementoObject() {
        return new Memento(state);
    }

    /**
     * 将发起人恢复到备忘录对象所记载的状态
     */
    public void restoreMemento(Memento memento) {
        this.state = memento.getState();
        System.out.println("恢复 备忘录 状态:" + state);
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

}

“黑箱”备忘录模式的实现   备忘录角色对发起人(Originator)角色对象提供一个宽接口,而为其他对象提供一个窄接口。这样的实现叫做“黑箱实现”。    在JAVA语言中,实现双重接口的办法就是将备忘录角色类设计成发起人角色类的内部成员类。    将Memento设成Originator类的内部类,从而将Memento对象封装在Originator里面;在外部提供一个标识接口 MementoIF给Caretaker以及其他对象。这样,Originator类看到的是Menmento的所有接口,而Caretaker以及其他 对象看到的仅仅是标识接口MementoIF所暴露出来的接口。

/**
 * 类似黑箱备忘录模式
 * 
 * 
 */
public class Client {

    /**
     * 客户端
     * 
     * @param args
     */
    public static void main(String[] args) {

        int state = 3;
        Originator originator = new Originator();
        Caretaker caretaker = new Caretaker();

        originator.setState(state);
        /**
         * 创建备忘录对象的 缓存起来
         */
        caretaker.saveMemento(originator.creatMementoObject());
        /*
         * 进行设置重新还原
         */
        originator.setState(5);
        System.out.println(" 黑箱发起人更改状态:" + originator.getState());

        originator.restoreMemento(caretaker.retrieveMemento());

    }

}

 

public interface MemotoIF {

}

 

/**
 * 发起者
 * 
 */
public class Originator {

    private int state = 0;

    Caretaker caretaker = new Caretaker();

    public Memento creatMementoObject() {
        return new Memento(state);
    }

    /**
     * 将发起人恢复到备忘录对象所记载的状态
     */
    public void restoreMemento(MemotoIF momIf) {
        this.setState(((Memento) momIf).getState());
        System.out.println("黑箱恢复 备忘录 状态:" + state);
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    private class Memento implements MemotoIF {

        private int state;

        public Memento(int state) {
            this.state = state;
            System.out.println("黑箱备忘录 当前保存 状态:" + state);
        }

        public int getState() {
            return state;
        }

        public void setState(int state) {

            this.state = state;
        }

    }

}

 

/**
 * 管理者 负责管理Caretaker
 * 
 * 
 */
public class Caretaker {

    private MemotoIF memento;

    /**
     * 备忘录的取值方法
     */
    public MemotoIF retrieveMemento() {
        return this.memento;
    }

    /**
     * 备忘录的赋值方法
     */
    public void saveMemento(MemotoIF memento) {
        this.memento = memento;
    }

3.多重检查点   前面所给出的白箱和黑箱的示意性实现都是只存储一个状态的简单实现,也可以叫做只有一个检查点。常见的系统往往需要存储不止一个状态,而是需要存储多个状态,或者叫做有多个检查点。    备忘录模式可以将发起人对象的状态存储到备忘录对象里面,备忘录模式可以将发起人对象恢复到备忘录对象所存储的某一个检查点上。

4.”自述历史”模式   所谓“自述历史”模式(History-On-Self Pattern)实际上就是备忘录模式的一个变种。在备忘录模式中,发起人(Originator)角色、负责人(Caretaker)角色和备忘录 (Memento)角色都是独立的角色。虽然在实现上备忘录类可以成为发起人类的内部成员类,但是备忘录类仍然保持作为一个角色的独立意义。在“自述历 史”模式里面,发起人角色自己兼任负责人角色。          

优点

​ 1、 给用户提供了一种可以恢复状态的机制。可以是用户能够比较方便地回到某个历史的状态。

​ 2、 实现了信息的封装。使得用户不需要关心状态的保存细节。

缺点

​ 消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。

 模式适用场景

​ 1、 需要保存一个对象在某一个时刻的状态或部分状态。

​ 2、 如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过负责人可以间接访问其内部状态。

模式总结

​ 1、 备忘录模式可以实现在不破坏封装的前提下,捕获一个类的内部状态,并且在该对象之外保存该对象的状态,保证该对象能够恢复到历史的某个状态。

​ 2、 备忘录模式实现了内部状态的封装,除了创建它的原发器之外其他对象都不能够访问它。

​ 3、 备忘录模式会占用较多的内存,消耗资源。

关注
打赏
1638062488
查看更多评论
立即登录/注册

微信扫码登录

0.0420s