备忘录模式

2022-10-25 18:34:16 浏览数 (1)

备忘录模式

备忘录模式是一个类的状态的恢复,由于单一职责,所以这功能不放在类自己内部,而是单独列出来,然后类内部持有一份备忘录对象。然后还要一个管理备忘录的对象,让外部场景类调用。

定义是“在不破坏封装性的前提下,捕获一个对象的内部状 态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。”

代码语言:javascript复制
public class Originator { // 要用到备忘录恢复 state 的类
    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 Client { // 场景类
    public static void main(String[] args) {
        Originator originator = new Originator();
        Memento mem = originator.createMemento();
        originator.restoreMemento(mem);
    } 
}

然后书上说这不符合迪米特原则呀,作为场景类 Client,就跟 Originator 打交道就行了,干嘛还要自己去管理备忘录 Memento 呢?到这我还能理解,然后说要新建一个管理者

代码语言:javascript复制
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());
    } 
}

这我没懂,加了一层到底优化在哪儿了,不和 Memento 打交道了,但你不是又何 Caretaker 产生耦合了吗?Caretaker 比 Memento 高贵在哪了?

原型模式实现的备忘录

代码语言:javascript复制
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;
        }
    }
}

// 到这他又把 Caretaker 去了
public class Client {  
    public static void main(String[] args) {
        Originator originator = new Originator();  
        originator.createMemento();  
        originator.restoreMemento(); 
    }
}

用 clone 状态在自己内部,没有通过单独一个对象去保存。因为涉及浅拷贝问题,所以类复杂时可能会导致这种方法也变得很复杂,所以说用在简单的场合。

多属性备忘

通过 Java 的 Introspector 类获取所有属性和值,然后存储到 HashMap 中。

代码语言:javascript复制
public class Originator { //内部状态
    private String state1 = ""; 
    private String state2 = ""; 
    private String state3 = ""; 
    // getter/setter

    //创建一个备忘录  
    public Memento createMemento(){
        return new Memento(BeanUtils.backupProp(this));
    }  

    //恢复一个备忘录  
    public void restoreMemento(Memento _memento){
        BeanUtils.restoreProp(this, _memento.getStateMap());
    }  
}

public class BeanUtils {  
    public static HashMap<String,Object> backupProp(Object bean){
        HashMap<String,Object> result = new HashMap<String,Object>(); 
        try {
            //获得Bean描述  
            BeanInfo beanInfo=Introspector.getBeanInfo(bean.getClass()); //获得属性描述  
            PropertyDescriptor[] descriptors=beanInfo.getPropertyDescrip //遍历所有属性  
            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<String,Object> 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<String,Object> stateMap; 
    public Memento(HashMap<String,Object> map){
        this.stateMap = map;
    }  
    // getter/setter
}

多备份

到了需要多备份,感觉是需要一个 Caretaker 了,Client 去管理就不好了。

代码语言:javascript复制
public class Caretaker { 
    // 多个备份,时间戳作为键
    private HashMap<String,Memento> memMap = new HashMap<String,Memento>(); 
    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"));
    } 
}

备份一旦产生就会装入内存,有可能产生内存溢出。所以严格限制备忘录的创建。

权限

备份不许篡改,权限要小。将原来的 Memento 写到 Originator 内部去,作为私有的内部类,让它实现一个空接口 IMemento,这样只有 Originator 能读写,而外面的其它交互都用接口,而由于接口是空的,就是用反射也没什么方法可以调用。

0 人点赞