`

《设计模式》之十七:备忘录模式

阅读更多

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

Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later

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

通俗讲,备忘录模式就是一个对象的备份模式,提供了一种程序数据的备份方法。

 

备忘录模式的三个角色定义:

1,Originator发起人角色

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

2,Memento备忘录角色

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

3,Caretaker备忘录管理员角色

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

 

通用模板代码:

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

 

备忘录模式的扩展,利用Java的clone方法可以将上述的三个角色融合到一个类中去:

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() {
        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;
    }
}

 注意:使用Clone模式的备忘录模式,应该在比较简单的场景或者比较单一的场景,尽量不要与其他对象产生严重耦合关系,因为浅拷贝和深拷贝的关系,你懂得。

 

备忘录模式使用场景:

1,需要保存和恢复数据的相关状态场景

2,提供一个可以回滚rollback的操作,比如Word中的CTRL+Z组合键,浏览器中的后退键,文件管理器的backspace键等。

3,需要监控的副本场景中,监控一般都是备份一个主线程中的对象,然后由分析程序来分析。

4,数据库事务管理的备忘录模式

 

备忘录模式注意事项:

1,备忘录的生命周期,备忘录创建出来就要在最近的代码中使用,要主动管理它的生命周期,建立就使用,不适用就立即删除其引用,等垃圾回收器去回收内存

2,备忘录的性能,不要在频繁建立备份的场景中使用备忘录模式比如一个for循环中。

 

多状态的备忘录模式

上面所说的都只有一个状态state,这在实际项目中是很少出现的,多状态下,我们写个通用的模板:

public class BeanUtils {
    /**
     * 把bean所有属性放入HashMap中去
     * @param bean
     * @return
     */
    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.getPropertyDescriptors();
            // 遍历所有属性
            for (PropertyDescriptor des : descriptors) {
                // 属性名称
                String fieldName = des.getName();
                // 读取属性的方法
                Method getter = des.getReadMethod();

                if (!fieldName.equalsIgnoreCase("class")) {
                    Object fieldValue = getter.invoke(bean);
                    result.put(fieldName, fieldValue);
                }
            }
        } catch (Exception e) {
            // 异常处理
        }
        return result;
    }

    public static void restoreProp(Object bean, HashMap<String, Object> propMap) {
        try {
            // 获得Bean描述
            BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
            // 获取Bean属性描述
            PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
            // 遍历所有属性
            for (PropertyDescriptor des : descriptors) {
                // 属性名称
                String fieldName = des.getName();
                // 如果有这个属性
                if (propMap.containsKey(fieldName)) {
                    // 写属性方法
                    Method setter = des.getWriteMethod();
                    setter.invoke(bean, propMap.get(fieldName));
                }
            }
        } catch (Exception e) {
            // 处理异常
            System.out.println("fuck");
            e.printStackTrace();
        }
    }
}

 

public class Memento {
    // 接受HashMap作为状态
    private HashMap<String, Object> stateMap;
    public Memento(HashMap<String, Object> map) {
        this.stateMap = map;
    }
    public HashMap<String, Object> getStateMap() {
        return stateMap;
    }
    public void setStateMap(HashMap<String, Object> stateMap) {
        this.stateMap = stateMap;
    }
}

 

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 + "state2=" + state2 + "state3=" + state3;
    }
}

 

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

 场景类Client就不写了,太简单了。。。

 

多备份的备忘录:

系统管理员经常会遇到这种情况,不仅仅存在一个Backup而言,而是由很多保存点check point或者叫save point来保存各个时间戳或者各个阶段的备份。我们这里只需要修改下Caretaker和Client应用类即可:

public class Caretaker {
    // 备忘录容器
    private HashMap<String, Memento> memMap = new HashMap<String, Memento>();
    public Memento getMemento(String key) {
        return memMap.get(key);
    }
    public void setMemento(String key, Memento memento) {
        this.memMap.put(key, memento);
    }
}

 在Client类中,获取和设置备忘录的时候多传递一个参数key就行了,无需多讲了。

 

封装的更好点,完美设计:

在系统管理上,备份的数据是只读的,绝对不能修改的,那么我们的模式中的Memento角色就应该是只读的。

增加一个空的接口:

public interface IMemento {
}

 

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(IMemento _memento) {
        BeanUtils.restoreProp(this, ((Memento)_memento).getStateMap());
    }

    // 增加一个toString方法
    @Override
    public String toString() {
        return "state1=" + state1 + "state2=" + state2 + "state3=" + state3;
    }

    /**
     * 内部类,为了就是Memento的只读性
     */
    private class Memento implements IMemento{
        // 接受HashMap作为状态
        private HashMap<String, Object> stateMap;
        public Memento(HashMap<String, Object> map) {
            this.stateMap = map;
        }
        public HashMap<String, Object> getStateMap() {
            return stateMap;
        }
        public void setStateMap(HashMap<String, Object> stateMap) {
            this.stateMap = stateMap;
        }
    }
}

 

public class Caretaker {
    // 备忘录容器
    private HashMap<String, IMemento> memMap = new HashMap<String, IMemento>();
    public IMemento getMemento(String key) {
        return memMap.get(key);
    }
    public void setMemento(String key, IMemento memento) {
        this.memMap.put(key, memento);
    }
}

 

最佳实践:

备忘录模式是我们设计上的”月光宝盒“,可以让我们回到需要的年代,是程序的后悔药。

所以我们在设计的时候不要使用数据库的临时表作为缓存备份数据了,虽然是一个简单的方法,但是它加大了数据库操作的频繁度,最好的解决办法就是使用备忘录模式。

 

本人博客已搬家,新地址为:http://yidao620c.github.io/

分享到:
评论

相关推荐

    Java描述设计模式(24):备忘录模式.zip

    Java描述设计模式(24):备忘录模式

    设计模式-备忘录模式(讲解及其实现代码)

    备忘录模式是一种在软件工程中广泛使用的面向对象设计模式,它主要用来安全地保存对象的状态,以便在需要时能够恢复到先前的状态。备忘录模式的核心思想是封装对象的状态,将其保存到一个独立的对象(备忘录)中,而...

    设计模式之备忘录(memento)

    备忘录模式主要涉及三个角色: 1. **发起人(Originator)**:需要保存其内部状态的对象。它创建并存储备忘录,也可以从备忘录恢复状态。 2. **备忘录(Memento)**:存储发起人的内部状态。备忘录是发起人的私有...

    设计模式-备忘录

    备忘录模式是一种常用的设计模式,它在软件工程中用于保存对象的状态,以便在需要时恢复到之前的状态。这种模式的主要目标是实现数据的安全存储,同时保持对象的封装性,避免对外部对象直接访问其内部状态。在iOS...

    设计模式之备忘录模式

    备忘录模式(Memento Pattern)是软件设计模式中的一种,属于行为模式。它提供了一种方式来恢复对象到之前的状态,即“撤销”操作。备忘录模式的核心思想是保存对象的内部状态,以便在需要时能恢复到这个状态,而...

    C#面向对象设计模式纵横谈(21):(行为型模式) Memento 备忘录模式

    备忘录模式(Memento Pattern)是一种行为设计模式,它允许在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将对象恢复到先前的状态。这种模式在需要撤销/重做功能、...

    备忘录模式.rar备忘录模式.rarjava设计模式

    备忘录模式是一种行为设计模式,它允许在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将对象恢复到原先保存的状态。这种模式常用于需要记录用户操作历史或者游戏进度...

    Java设计模式-备忘录

    **Java设计模式——备忘录模式详解** 备忘录模式是一种行为设计模式,它允许对象在不破坏封装性的前提下捕获并存储其内部状态,以便稍后恢复到该状态。这种模式常用于在游戏中保存进度、撤销/重做操作、以及在复杂...

    java设计模式-备忘录模式源代码

    备忘录模式是一种在软件工程中广泛使用的面向对象设计模式,它主要用来安全地保存对象的状态,以便在需要时能够恢复到先前的状态。这个模式的名字来源于我们日常生活中使用的备忘录,它记录了一些重要的信息,当需要...

    设计模式之备忘录模式(Memento Pattern)

    备忘录模式(Memento Pattern)是软件设计模式中的一种行为模式,它的主要目的是在不破坏对象封装性的前提下,允许对象在特定时刻保存其内部状态,并能够在之后恢复到保存时的状态。这种模式广泛应用于撤销/重做功能...

    设计模式之备忘录模式,内含可运行代码及详细解释

    备忘录模式是一种常用的设计模式,它在软件工程中扮演着重要的角色,特别是在需要保护对象内部状态不被外部篡改的场景下。备忘录模式的核心思想是提供一种方法来捕获一个对象的内部状态,并在该对象之外保存这个状态...

    第二十二讲:备忘录模式

    备忘录模式是一种设计模式,它允许在不破坏封装性的前提下,捕获并存储一个对象的内部状态,以便在需要时能恢复到该状态。这个模式的核心思想是实现对象状态的保存与恢复,通常用于撤销/重做操作或者游戏存档等功能...

    微信小程序推荐demo:备忘录

    "微信小程序推荐demo:备忘录"是一个示例项目,旨在帮助开发者了解和学习如何利用微信小程序框架开发备忘录类应用。 备忘录小程序通常包含以下几个核心功能模块: 1. **用户界面**:设计简洁易用的用户界面是关键...

    设计模式_备忘录模式.zip

    在Java中,我们可以这样实现备忘录模式: ```java // 发起人角色 class Originator { private String state; // 需要保存的状态 public Originator(String state) { this.state = state; } // 创建备忘录 ...

    设计模式之备忘录模式的Java版本实现

    备忘录模式是一种在软件设计中广泛使用的结构型设计模式,它的主要目的是为了保存对象的状态,以便在需要的时候能够恢复到之前的状态。备忘录模式的核心思想是通过创建一个备忘录对象来存储原对象的状态,这个备忘录...

    设计模式之备忘录模式(Memento)

    备忘录模式(Memento Pattern)是设计模式中的一种行为模式,主要目的是在不违反封装原则的情况下,保存一个对象的内部状态,以便在需要时能够恢复到先前的状态。这种模式通常用于实现撤销/重做功能或者在游戏中保存...

    设计模式_行为型_备忘录模式.md

    备忘录模式(Memento)是一种行为型设计模式,它允许在不暴露对象的实现细节的情况下,捕获并保存对象的内部状态,从而可以在未来某个时刻恢复到之前的状态。备忘录模式特别适用于实现撤销操作,或是需要进行状态...

    设计模式 - 备忘录模式(C++实例)

    备忘录模式是一种在不破坏封装性的前提下,捕获一个对象的状态,并允许在未来的某个时刻恢复这个状态的设计模式。这种模式在很多场景下都非常有用,例如在游戏中保存进度、在编辑器中撤销/重做操作等。备忘录模式的...

    23种设计模式之备忘录模式

    备忘录模式是一种行为设计模式,它允许在不破坏对象封装性的前提下,保存和恢复对象的状态。在软件开发中,这种模式常用于实现撤销/重做功能或者在需要保存临时状态的情况下。以下是对备忘录模式的详细解释: 1. **...

Global site tag (gtag.js) - Google Analytics