18-备忘录模式

1.定义

备忘录模式(Memento Pattern)提供了一种弥补真实世界缺陷的方法,让“后悔药”在程序的世界中真实可行,其定义如下:

Without violating encapsulation,capture and externalize an object’s internal state so that the object can be restored to this state later.(在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。)


2.类图

18-备忘录模式_第1张图片

  • Originator发起人角色

记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。

  • Memento备忘录角色

负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。

  • Caretaker备忘录管理员角色

对备忘录进行管理、保存和提供备忘录。


3.代码

/**
* 发起人角色
*/

public class Originator {
    //内部状态
    private String state = "";
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
    //创建一个备忘录
    public Memento createMemento(){
        return new Memento(this.state);
    }
    //恢复一个备忘录
    public void restoreMemento(Memento _memento){
        this.setState(_memento.getState());
    }
}
/**
* 备忘录角色
*/

public class Memento {
    //发起人的内部状态
    private String state = "";
    //构造函数传递参数
    public Memento(String _state){
        this.state = _state;
    }
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
}
/**
* 备忘录管理员角色
*/

public class Caretaker {
    //备忘录对象
    private Memento memento;
    public Memento getMemento() {
        return memento;
    }
    public void setMemento(Memento memento) {
        this.memento = memento;
    }
}
/**
* 场景类
*/

public class Client {
    public static void main(String[] args) {
        //定义出发起人
        Originator originator = new Originator();
        //定义出备忘录管理员
        Caretaker caretaker = new Caretaker();
        //创建一个备忘录
        caretaker.setMemento(originator.createMemento());
        //恢复一个备忘录
        originator.restoreMemento(caretaker.getMemento());
    }
}

4.使用场景

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

5.注意事项

  • 备忘录的生命期

备忘录创建出来就要在“最近”的代码中使用,要主动管理它的生命周期,建立就要使用,不使用就要立刻删除其引用,等待垃圾回收器对它的回收处理。

  • 备忘录的性能

不要在频繁建立备份的场景中使用备忘录模式(比如一个for循环中),原因有二:一是控制不了备忘录建立的对象数量;二是大对象的建立是要消耗资源的,系统的性能需要考虑。因此,如果出现这样的代码,设计师就应该好好想想怎么修改架构了。


6.扩展

6.1 clone方式的备忘录

18-备忘录模式_第2张图片

从类图上看,发起人角色融合了发起人角色和备忘录角色,具有双重功效。

/**
* 融合备忘录的发起人角色
*/

public class Originator implements Cloneable{
    //内部状态
    private String state = "";
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
    //创建一个备忘录
    public Originator createMemento(){
        return this.clone();
    }
    //恢复一个备忘录
    public void restoreMemento(Originator _originator){
        this.setState(_originator.getState());
    }
    //克隆当前对象
    @Override
    protected Originator clone(){
        try {
            return (Originator)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}
/**
* 备忘录管理员角色
*/

public class Caretaker {
    //发起人对象
    private Originator originator;
    public Originator getOriginator() {
        return originator;
    }
    public void setOriginator(Originator originator) {
        this.originator = originator;
    }
}
/**
* 发起人自主备份和恢复
*/

public class Originator implements Cloneable{
    private Originator backup;
    //内部状态
    private String state = "";
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
    //创建一个备忘录
    public void createMemento(){
        this.backup = this.clone();
    }
    //恢复一个备忘录
    public void restoreMemento(){
        //在进行恢复前应该进行断言,防止空指针
        this.setState(this.backup.getState());
    }
    //克隆当前对象
    @Override
    protected Originator clone(){
        try {
            return (Originator)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}
/**
* 场景类
*/

public class Client {
    public static void main(String[] args) {
        //定义发起人
        Originator originator = new Originator();
        //建立初始状态
        originator.setState("初始状态...");
        System.out.println("初始状态是:"+originator.getState());
        //建立备份
        originator.createMemento();
        //修改状态
        originator.setState("修改后的状态...");
        System.out.println("修改后状态是:"+originator.getState());
        //恢复原有状态
        originator.restoreMemento();
        System.out.println("恢复后状态是:"+originator.getState());
    }
}

初始状态是:初始状态…
修改后状态是:修改后的状态…
恢复后状态是:初始状态…

注意 使用Clone方式的备忘录模式,可以使用在比较简单的场景或者比较单一的场景中,尽量不要与其他的对象产生严重的耦合关系。

6.2 多状态的备忘录模式

18-备忘录模式_第3张图片

/**
* 发起人角色
*/

public class Originator {
    //内部状态
    private String state1 = "";
    private String state2 = "";
    private String state3 = "";
    public String getState1() {
        return state1;
    }
    public void setState1(String state1) {
        this.state1 = state1;
    }
    public String getState2() {
        return state2;
    }
    public void setState2(String state2) {
        this.state2 = state2;
    }
    public String getState3() {
        return state3;
    }
    public void setState3(String state3) {
        this.state3 = state3;
    }
    //创建一个备忘录
    public Memento createMemento(){
        return new Memento(BeanUtils.backupProp(this));
    }
    //恢复一个备忘录
    public void restoreMemento(Memento _memento){
        BeanUtils.restoreProp(this, _memento.getStateMap());
    }
    //增加一个toString方法
    @Override
    public String toString(){
        return "state1=" +state1+"\nstat2="+state2+"\nstate3="+state3;
    }
}
/**
* BeanUtils工具类
*/

public class BeanUtils {
    //把bean的所有属性及数值放入到Hashmap中
    public static HashMap backupProp(Object bean){
        HashMap result = new HashMap();
        try {
            //获得Bean描述
            BeanInfo beanInfo=Introspector.getBeanInfo(bean.getClass());
            //获得属性描述
            PropertyDescriptor[] descriptors=beanInfo.getPropertyDescriptors();
            //遍历所有属性
            for(PropertyDescriptor des:descriptors){
                //属性名称
                String fieldName = des.getName();
                //读取属性的方法
                Method getter = des.getReadMethod();
                //读取属性值
                Object fieldValue=getter.invoke(bean,new Object[]{});
                if(!fieldName.equalsIgnoreCase("class")){
                    result.put(fieldName, fieldValue);
                }
            }
        } catch (Exception e) {
            //异常处理
        }
        return result;
    }
    //把HashMap的值返回到bean中
    public static void restoreProp(Object bean,HashMap propMap){
        try {
            //获得Bean描述
            BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
            //获得属性描述
            PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
            //遍历所有属性
            for(PropertyDescriptor des:descriptors){
                //属性名称
                String fieldName = des.getName();
                //如果有这个属性
                if(propMap.containsKey(fieldName)){
                    //写属性的方法
                    Method setter = des.getWriteMethod();
                    setter.invoke(bean, new Object[]{propMap.get(fieldName)});
                }
            }
        } catch (Exception e) {
            //异常处理
            System.out.println("shit");
            e.printStackTrace();
        }
    }
}
/**
* 备忘录角色
*/

public class Memento {
    //接受HashMap作为状态
    private HashMap stateMap;
    //接受一个对象,建立一个备份
    public Memento(HashMap map){
        this.stateMap = map;
    }
    public HashMap getStateMap() {
        return stateMap;
    }
    public void setStateMap(HashMap stateMap) {
        this.stateMap = stateMap;
    }
}
/**
* 场景类
* /

public class Client {
    public static void main(String[] args) {
        //定义出发起人
        Originator ori = new Originator();
        //定义出备忘录管理员
        Caretaker caretaker = new Caretaker();
        //初始化
        ori.setState1("中国");
        ori.setState2("强盛");
        ori.setState3("繁荣");
        System.out.println("===初始化状态===\n"+ori);
        //创建一个备忘录
        caretaker.setMemento(ori.createMemento());
        //修改状态值
        ori.setState1("软件");
        ori.setState2("架构");
        ori.setState3("优秀");
        System.out.println("\n===修改后状态===\n"+ori);
        //恢复一个备忘录
        ori.restoreMemento(caretaker.getMemento());
        System.out.println("\n===恢复后状态===\n"+ori);
    }
}

运行结果如下所示:
===初始化状态===
state1=中国
stat2=强盛
state3=繁荣
===修改后状态===
state1=软件
stat2=架构
state3=优秀
===恢复后状态===
state1=中国
stat2=强盛
state3=繁荣

注意 如果要设计一个在运行期决定备份状态的框架,则建议采用AOP框架来实现,避免采用动态代理无谓地增加程序逻辑复杂性。

6.3 多备份的备忘录

/**
* 备忘录管理员
*/

public class Caretaker {
    //容纳备忘录的容器
    private HashMap memMap = new HashMap();
    public Memento getMemento(String idx) {
        return memMap.get(idx);
    }
    public void setMemento(String idx,Memento memento) {
        this.memMap.put(idx, memento);
    }
}
/**
* 场景类
*/

public class Client {
    public static void main(String[] args) {
        //定义出发起人
        Originator originator = new Originator();
        //定义出备忘录管理员
        Caretaker caretaker = new Caretaker();
        //创建两个备忘录
        caretaker.setMemento("001",originator.createMemento());
        caretaker.setMemento("002",originator.createMemento());
        //恢复一个指定标记的备忘录
        originator.restoreMemento(caretaker.getMemento("001"));
    }
}

注意 内存溢出问题,该备份一旦产生就装入内存,没有任何销毁的意向,这是非常危险的。因此,在系统设计时,要严格限定备忘录的创建,建议增加Map的上限,否则系统很容易产生内存溢出情况。

6.4 封装得更好一点

18-备忘录模式_第4张图片

/**
* 发起人角色
*/

public class Originator {
    //内部状态
    private String state = "";
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
    //创建一个备忘录
    public IMemento createMemento(){
        return new Memento(this.state);
    }
    //恢复一个备忘录
    public void restoreMemento(IMemento _memento){
        this.setState(((Memento)_memento).getState());
    }
    //内置类
    private class Memento implements IMemento{
        //发起人的内部状态
        private String state = "";
        //构造函数传递参数
        private Memento(String _state){
            this.state = _state;
        }
        private String getState() {
            return state;
        }
        private void setState(String state) {
            this.state = state;
        }
    }
}
/**
* 备忘录的空接口
*/

public interface IMemento {
}
/**
* 备忘录管理者
*/

public class Caretaker {
    //备忘录对象
    private IMemento memento;
    public IMemento getMemento() {
        return memento;
    }
    public void setMemento(IMemento memento) {
        this.memento = memento;
    }
}

你可能感兴趣的:(设计模式)