Design Patterns - Memorandum Patterns

Posted by Tim Silva on Fri, 09 Aug 2019 07:58:28 +0200

Articles Catalogue

Definition

Without destroying the encapsulation, capture the internal state of an object and save it outside the object. In this way, the object can be restored to its original saved state.

Type: Behavior class pattern

Class diagram:

When we program, we often need to save the intermediate state of the object, which can be restored when needed. For example, when we use Eclipse to program, if we make mistakes (such as accidentally deleting a few lines of code), we want to return to the state before deletion, we can use Ctrl+Z to return. At this point, we can use the memorandum mode to achieve.

structure

  • Originator: Record the internal status of the current moment, define which States belong to the backup scope, and create and restore the memo data.
  • Memorandum: It is responsible for storing the internal state of the sponsor object and providing the internal state needed by the sponsor when needed.
  • Management role: Manage, save and provide memos.

code implementation

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

    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;
        }
    }
    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();
            originator.setState("State 1");
            System.out.println("Initial state:"+originator.getState());
            Caretaker caretaker = new Caretaker();
            caretaker.setMemento(originator.createMemento());
            originator.setState("State 2");
            System.out.println("Post-change status:"+originator.getState());
            originator.restoreMemento(caretaker.getMemento());
            System.out.println("Post-recovery status:"+originator.getState());
        }
    }

The code demonstrates an example of a single-state single backup. The logic is very simple: the state variable in the Originator class needs to be backed up to recover when needed; the state variable in the Memento class also has a state variable to store the temporary state of the state variable in the Originator class; and the Caretaker class is used to manage the memo class. To write or retrieve state from a memo object.

Multi-state multi-backup memorandum
In the general code demonstration example, the Originator class has only one state variable to backup, and usually the initiator role is a javaBean. There are more than one variables to backup in the object and more than one state to backup. This is a multi-state multi-backup memorandum.

There are many ways to implement the memo, and the memo mode has a lot of deformation and processing methods. It is not usually used in the way of general code. In most cases, the memo mode is multi-state and multi-backup. In fact, it is very simple to implement multi-state and multi-backup. The most commonly used method is to add a Map container to Memento to store all states, and also use a Map container to store all backups in the Caretaker class. Let's give an example of multi-state and multi-backup:

    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());
        }
        public String toString(){
            return "state1="+state1+"state2="+state2+"state3="+state3;
        }
    }
    class Memento {
        private Map stateMap;

        public Memento(Map map){
            this.stateMap = map;
        }

        public Map getStateMap() {
            return stateMap;
        }

        public void setStateMap(Map stateMap) {
            this.stateMap = stateMap;
        }
    }
    class BeanUtils {
        public static Map backupProp(Object bean){
            Map result = new HashMap();
            try{
                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){
                e.printStackTrace();
            }
            return result;
        }

        public static void restoreProp(Object bean, Map propMap){
            try {
                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) {
                e.printStackTrace();
            }
        }
    }
    class Caretaker {
        private Map memMap = new HashMap();
        public Memento getMemento(String index){
            return memMap.get(index);
        }

        public void setMemento(String index, Memento memento){
            this.memMap.put(index, memento);
        }
    }
    class Client {
        public static void main(String[] args){
            Originator ori = new Originator();
            Caretaker caretaker = new Caretaker();
            ori.setState1("China");
            ori.setState2("Strong and prosperous");
            ori.setState3("Boom");
            System.out.println("===Initialization state===n"+ori);

            caretaker.setMemento("001",ori.createMemento());
            ori.setState1("Software");
            ori.setState2("Framework");
            ori.setState3("excellent");
            System.out.println("===Modified status===n"+ori);

            ori.restoreMemento(caretaker.getMemento("001"));
            System.out.println("===Post-recovery status===n"+ori);
        }
    }

Advantages and disadvantages

Advantage

  • When the status changes in the sponsor role, it may be a wrong change. We can restore the wrong change by using the memo mode.
  • The backup status is stored outside the initiator role, so that the initiator role does not need to manage the status of each backup.

shortcoming

  • In practical applications, the memorandum mode is multi-state and multi-backup. The status of the initiator role needs to be stored in the object of the memorandum, and the consumption of resources is serious.

Use scenarios

If there is a need to provide rollback operations, the use of memo mode is very suitable, such as jdbc transaction operations, text editor Ctrl+Z recovery and so on.

Topics: Eclipse JDBC