备忘录模式(Cemento Pattern)

一、定义
在不破坏封装性的前提下, 捕获一个对象的内部状态, 并在该对象之外保存这个状态。 这样以后就可将该对象恢复到原先保存的状态。
二、示例
相信我们在工作中都用过word编辑器吧,word中有一项功能,当我们的编辑出错时,Ctrl+Z可以使我们回退到以前的文本,这样我们就可以在以前文本的基础上继续编辑,而不需要从头开始编辑。现在我们应用备忘录模式实现一下这个功能。
类图如下:
这里写图片描述

首先我们要创建一个文档编辑器,负责编辑文本,备份文本,及恢复备份的文本

package memento;
/**
 * 文档编辑器
 */
public class Word {

    private String text;//文本

    //获取文本内容
    public String getText() {
        return text;
    }

    //编辑文本内容
    public void setText(String text) {
        this.text = text;
    }

    //创建一个备忘录,并且保存文本到备忘录中
    public Memento createMemento(String text){
        return new Memento(text);
    }

    //从备忘录中恢复文本
    public void restoreText(Memento memento){
        this.setText(memento.getText());
    }

}

其次我们要创建一个备忘录类,保存我们备份的文本

package memento;
/**
 * 备忘录类
 */
public class Memento {

    private String text;//备份的文本

    //构造方法,传入要备份的文本
    public Memento(String text) {
        super();
        this.text = text;
    }

    //获取备份文本
    public String getText() {
        return text;
    }

    //备份文本到备忘录中
    public void setText(String text) {
        this.text = text;
    }

}

我们还要创建一个备忘录管理类,来管理我们的备忘录

package memento;

import java.util.HashMap;

/**
 * 备忘录管理类
 */
public class MementoManager {

    private HashMap<String,Memento> mementos=new HashMap<String,Memento>();//保存备忘录,根据时间来区分备忘录

    //添加备忘录
    public void addMemento(String time,Memento memento){
        if(this.mementos==null){
            this.mementos=new HashMap<String,Memento>();
        }
        this.mementos.put(time,memento);
    }

    //根据时间获取备忘录
    public Memento getMemento(String time){
        return this.mementos.get(time);
    }
}

测试类

package memento;
/**
 * 场景类
 */
public class Client {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Word word=new Word();//新建一个文档
        MementoManager manager=new MementoManager();//备份管理器
        word.setText("第一行文本");//写入第一行文本
        System.out.println("写入文本,当前文本:\n"+word.getText());
        manager.addMemento("15:38",word.createMemento(word.getText()));//创建一个备份并添加到管理器中
        System.out.println("======备份======");
        word.setText("第一行文本\n第二行文本");//写入第二行文本
        System.out.println("写入文本,当前文本:\n"+word.getText());
        word.restoreText(manager.getMemento("15:38"));
        System.out.println("恢复,当前文本:\n"+word.getText());
    }

}

运行结果:

这里写图片描述

三、角色

通俗地说, 备忘录模式就是一个对象的备份模式, 提供了一种程序数据的备份方法。一般来说,有三种角色:

  1. Originator 发起人角色
    记录当前时刻的内部状态, 负责定义哪些属于备份范围的状态, 负责创建和恢复备忘录数据。例如上例中的:Word.class。
  2. Cementor 备忘录角色
    负责存储Originator发起人对象的内部状态, 在需要的时候提供发起人需要的内部状态。例如上例中的:Cementor.class。
  3. CementorManager 备忘录管理员角色
    对备忘录进行管理、 保存和提供备忘录。例如上例中的:CementorManager.class。

四、使用场景

  • 需要保存和恢复数据的相关状态场景。
  • 提供一个可回滚( rollback) 的操作; 比如Word中的CTRL+Z组合键, IE浏览器中的后退按钮, 文件管理器上的backspace键等。
  • 需要监控的副本场景中。 例如要监控一个对象的属性, 但是监控又不应该作为系统的主业务来调用, 它只是边缘应用, 即使出现监控不准、 错误报警也影响不大, 因此一般的做法是备份一个主线程中的对象, 然后由分析程序来分析。

五、注意事项

  1. 备忘录的生命期
    备忘录创建出来就要在“最近”的代码中使用, 要主动管理它的生命周期, 建立就要使用, 不使用就要立刻删除其引用, 等待垃圾回收器对它的回收处理。
  2. 备忘录的性能
    不要在频繁建立备份的场景中使用备忘录模式( 比如一个for循环中) , 原因有二: 一是控制不了备忘录建立的对象数量; 二是大对象的建立是要消耗资源的, 系统的性能需要考虑。

六、clone方式的备忘录

原型模式的时候,我们可以通过复制的方法产生一个对象的内部状态,这个一个很好的方法,我们只需要发起者角色实现Cloneable接口:

package memento;
/**
 * 实现Cloneable接口,既是发起者角色,也是备忘录角色
 */
public class CloneableWord implements Cloneable{

    private String text;//文本

    private CloneableWord backup;//备份对象

    //获取文本内容
    public String getText() {
        return text;
    }

    //设置文本内容
    public void setText(String text) {
        this.text = text;
    }

    //创建备份
    public void createMemento(){
        this.backup=(CloneableWord) this.clone();//通过clone()方法复制对象的状态给备份对象
    }

    //从备份中恢复文本
    public void restoreText(){
        this.setText(this.backup.getText());//从备份对象恢复对象状态
    }

    @Override
    protected Object clone() {
        try{
            return (CloneableWord)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        return null;
    }
}

增加一个备份对象,通过clone()方法完整复制一个对象的状态到备份对象,需要的时候再通过备份对象还原。这样,CloneableWord既是发起人角色,也是备忘录角色。

package memento;
/**
 * 场景类
 */
public class Client {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        CloneableWord word=new CloneableWord();//新建一个文档
        word.setText("第一行文本");//写入第一行文本
        System.out.println("写入文本,当前文本:\n"+word.getText());
        word.createMemento();//备份
        System.out.println("======备份======");
        word.setText("第一行文本\n第二行文本");//写入第二行文本
        System.out.println("写入文本,当前文本:\n"+word.getText());
        word.restoreText();//恢复文本
        System.out.println("恢复,当前文本:\n"+word.getText());
    }

}

运行结果:
这里写图片描述

与前面实现方式完全一致。

项目地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章