设计模式~备忘录模式

备忘录模式(Memento Pattern)又叫做快照模式(Snapshot Pattern)或Token模式,是对象的行为模式。

备忘录对象是一个用来存储另外一个对象内部状态的快照的对象

备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉住,并外部化存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。

备忘录模式常常和命令模式和迭代器模式一同使用。

存储这些快照的备忘录对象叫做此对象的历史;某个快照所处的位置叫做检查点。

备忘录角色(Memento)

备忘录角色由如下责任:

1. 将发起人(Originator)对象的内部状态存储起来。备忘录可以根据发起人对象的判断来决定存储多少发起人对象的内部状态。

2.备忘录可以保护器内容不被发起人(Originator)对象之外的任何对象所读取。

备忘录有两个等效的接口:

  窄接口:负责人(Caretaker)对象(和其他除发起人之外的任何对象)看到的是备忘录的窄接口(narrow interface),这个窄接口只允许它把备忘录对象传给其他的对象。

  宽接口:与负责人对象看到的窄接口相反的是,发起人对象可以看到一个宽接口(wide interface),这个宽接口允许它读取所有的数据,以便根据这些数据恢复对这个发起人对象的内部状态。

发起人角色(Originator)

发起人角色有如下责任:

  • 创建一个含有当前的内部状态的备忘录对象
  • 使用备忘录对象存储其内部状态。

负责人角色(Caretaker)

负责人角色有如下责任:

  • 负责保存备忘录对象
  • 不检查备忘录对象的内容

备忘录模式的白箱实现

 

 

 源代码清单:

 发起人角色

public class Originator {

    private String state;
    /**
     * 工厂方法,返还一个新的备忘录对象
     * @return
     */
    public Memento createMemento(){
        return new Memento(state);
    }
    /**
     * 将发起人恢复到备忘录对象所记载的状态
     * @param memento
     */
    public void restoreMemento(Memento memento){
        this.state = memento.getState();
    }
    
    public String getState(){
        return this.state;
    }
    public void setState(String state){
        this.state = state;
        System.out.println("Current state = "+this.state);
    }
}

备忘录角色

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;
    }
}

负责人角色

负责人角色负责保存备忘录对象,但是从不修改(甚至查看备忘录对象的内容)

public class Caretaker {

    private Memento memento;
    /**
     * 备忘录的取值方法
     * @return
     */
    public Memento retrieveMemento(){
        return this.memento;
    }
    /**
     * 备忘录的复制方法
     * @param memento
     */
    public void saveMemento(Memento memento){
        this.memento = memento;
    }
}

客户端

public class Client {

    private static Originator o = new Originator();
    private static Caretaker c = new Caretaker();
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        //改变发起人对象的状态
        o.setState("On");
        //创建备忘录对象,并将发起人对象的状态存储起来
        c.saveMemento(o.createMemento());
        //修改发起人对象的状态
        o.setState("Off");
        //恢复发起人对象的状态
        o.restoreMemento(c.retrieveMemento());
        
    }
}

白箱实现的活动时序

 

 可以看出,将发起人对象的状态存储到备忘录中时,系统的运行时序:

1.将发起人对象的状态设置为On

2.调用发起人角色的 createMemento() 方法,创建一个备忘录对象将这个状态存储起来

3.将备忘录对象存储到负责人对象中。

发起人对象恢复到备忘录对象所记录的状态时序图:

 

 1.将发起人对象的状态设置成 Off

 2. 将备忘录对象从负责人对象中取出

 3. 将发起人对象恢复到备忘录对象所存储起来的状态,即 On 状态。

白箱实现的优缺点

白箱实现的一个明显好处是比较简单,因此常常用作教学目的。

白箱实现的一个明显缺点是破坏对发起人状态的封装。

 

双重接口:所谓双重接口,就是对某一个对象提供宽接口,而对另一些对象提供窄接口。

备忘录模式的黑箱实现

 使用内部类实现备忘录模式的类图

 

 如果需要将内部类明显表示出来,那么类图可以重新绘制为:

 

 下面给出第一张类图的实现

发起人 Originator 角色源码:

public class Originator {

    private String state;
    public Originator(){
        
    }
    /**
     * 工厂方法,返还一个新的备忘录对象
     * @return
     */
    public MementoIF createMemento(){
        return new Memento(this.state);
    }
    //将发起人恢复到备忘录对象记录的状态
    public void restoreMemento(MementoIF memento){
        Memento aMemento = (Memento)memento;
        this.setState(aMemento.getState());
    }
    
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
        System.out.println("state="+state);
    }
    /**
     * 内部成员类,备忘录
     * @author Administrator
     *
     */
    protected class Memento implements MementoIF{
        private String savedState;
        private Memento(String someState){
            savedState = someState;
        }
        private void setState(String someState){
            savedState = someState;
        }
        private String getState(){
            return savedState;
        }
    }
}

MementoIF 角色

public interface MementoIF {

}

Caretaker 角色

public class Caretaker {

    private MementoIF memento;
    //备忘录的取值方法
    public MementoIF retrieveMemento(){
        return this.memento;
    }
    //备忘录的赋值方法
    public void saveMemento(MementoIF memento){
        this.memento = memento;
    }
}

Client角色

public class Client {

    private static Originator o = new Originator();
    private static Caretaker c = new Caretaker();
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        //改变发起人对象的状态
        o.setState("On");
        //创建备忘录对象,并将发起人对象的状态存储起来
        c.saveMemento(o.createMemento());
        //修改发起人对象的状态
        o.setState("Off");
        //恢复发起人对象的状态
        o.restoreMemento(c.retrieveMemento());
    }

}

备忘录模式与多重检查点

前面给出的白箱和黑箱的示意性实现都是只存储一个状态的简单实现,也可以叫做只有一个检查点。

常见的软件系统往往存储不止一个状态,而是需要存储多个状态或者叫做有多个检查点。

设计图:

 

 源码如下:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

备忘录模式的应用

JDBC与数据库

J2EE框架中备忘录模式的应用

这里讨论备忘录模式在Servlet技术中的应用。

Session对象,可以弥补HTTP协议的无状态的缺点,存储用户的状态。

 

 浏览器的Cookie文件

 

 备忘录模式的优缺点

优点

  • 有时一些发起人对象的内部信息必须保存在发起人对象以外的地方,但是必须要由发起人对象自己读取。这时,使用备忘录可以把复杂的发起人内部信息对其他的对象屏蔽起来,从而可以恰当的保持封装的边界。
  • 本模式简化了发起人类(Originator)。发起人不再需要管理和保持其内部状态的一个个版本,客户端可以自行管理他们所需要的这些状态的版本。
  • 当发起人角色的状态改变的时候,有可能这个状态无效,这时候可以使用暂时存储起来的备忘录将其状态恢复。

缺点:

  • 如果发起人角色的状态需要完整的存储到备忘录对象中,那么在资源耗费上面备忘录对象会很昂贵。
  • 当负责人角色将一个备忘录存储起来的时候,负责人可能并不知道这个状态会占用多大的存储空间,从而无法提醒用户一个操作是否会很昂贵。
  • 当发起人角色的状态改变的时候,有可能这个状态无效。如果状态改变的成功率不高的话,不如采取假如协议模式。

 

posted @ 2020-09-01 22:56  Vincent-yuan  阅读(237)  评论(0编辑  收藏  举报