Definition and Structure
Memento mode is also called Token mode. GOF defines the memo pattern as: capturing the internal state of an object and saving this state outside the object without destroying encapsulation. This allows you to later restore the object to its original saved state.
When talking about command mode, we once mentioned that the functions of undo and redo can be realized by using the middle command role. It can be seen from the definition that the memo mode is specially used to store the historical status of objects, which is very helpful for realizing the undo and redo functions. Therefore, in the command mode, the undo and redo functions can be implemented in conjunction with the memo mode.
In fact, it is very simple to realize the function of saving the state of an object at a certain moment - put the attributes to be saved in the object into an object specially managed for backup, and call it when needed The agreed method puts the backed-up properties back into the original object. But you need to take a closer look. In order to allow your backup object to access the properties of the original object, does it mean that you have to expose all the original private properties of the object or expose the original private properties of the object in the package? If your approach has broken encapsulation, then consider refactoring.
The memo mode is just a general solution proposed by GOF to the problem of "restoring the original state of an object at a certain time". Therefore, on how to maintain encapsulation - due to the influence of language characteristics and other factors, the memo mode is not described in detail, but the idea is explained based on C++.
1) Memento role: The memento role stores the internal state of the "memento initiating role". The "memo initiating role" determines which internal states of the "memo initiating role" the memo role stores as needed. To prevent objects other than the "memo initiating role" from accessing the memo. Memo actually has two interfaces. The "Memo Manager role" can only see the narrow interface provided by Memo - the attributes stored in the Memo role are not visible. The "memo initiating role" can see a wide interface - it can get the attributes that it puts into the memo role.
2) Memo Originator role: The "Memo Originator role" creates a memo to record its internal status at the current moment. Use memos to restore internal state when needed.
3) Caretaker role: Responsible for keeping good memos. The contents of the memo cannot be operated or inspected.
The class diagram of memo mode couldn’t be simpler:
General 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("状态1"); System.out.println("初始状态:"+originator.getState()); Caretaker caretaker = new Caretaker(); caretaker.setMemento(originator.createMemento()); originator.setState("状态2"); System.out.println("改变后状态:"+originator.getState()); originator.restoreMemento(caretaker.getMemento()); System.out.println("恢复后状态:"+originator.getState()); } }
The code demonstrates an example of single state and single backup. The logic is very simple: the state variable in the Originator class needs to be backed up so that it can be restored when needed; the Memento class also has a state variable used to store the state variables in the Originator class. The temporary state of the state variable; the Caretaker class is used to manage the memo class, and is used to write or retrieve status to the memo object.
Multi-state multiple backup memo
In the example of the general code demonstration, the Originator class has only one state variable that needs to be backed up. Under normal circumstances, the initiator role is usually a javaBean, and there are more than one variables in the object that need to be backed up. One, and there is more than one state that needs to be backed up. This is a multi-state multi-backup memo. There are many ways to implement memos. The memo pattern has many variations and processing methods. Methods like general code are generally not used. In most cases, the memo pattern is multi-state and multi-backup. In fact, it is very simple to implement multi-state and multiple backups. The most common method is that we add a Map container to Memento to store all states, and also use a Map container in the Caretaker class to store all backups. Below we give an example of multi-state 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<String, Object> stateMap; public Memento(Map<String, Object> map){ this.stateMap = map; } public Map<String, Object> getStateMap() { return stateMap; } public void setStateMap(Map<String, Object> stateMap) { this.stateMap = stateMap; } } class BeanUtils { public static Map<String, Object> backupProp(Object bean){ Map<String, Object> result = new HashMap<String, Object>(); 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<String, Object> 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<String, Memento> memMap = new HashMap<String, Memento>(); 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("中国"); ori.setState2("强盛"); ori.setState3("繁荣"); System.out.println("===初始化状态===\n"+ori); caretaker.setMemento("001",ori.createMemento()); ori.setState1("软件"); ori.setState2("架构"); ori.setState3("优秀"); System.out.println("===修改后状态===\n"+ori); ori.restoreMemento(caretaker.getMemento("001")); System.out.println("===恢复后状态===\n"+ori); } }
The advantages, disadvantages and applicable scenarios of memo mode
The advantages of memo mode are:
When the status in the initiator role changes, it may be a wrong change. We can use the memo mode to restore this wrong change.
The status of the backup is saved outside the initiator role, so the initiator role does not need to manage the status of each backup.
Disadvantages of the memo mode:
In practical applications, the memo mode is multi-state and multi-backup. The status of the initiator role needs to be stored in the memo object, which consumes resources seriously.
If there is a need to provide rollback operations, the memo mode is very suitable, such as jdbc transaction operations, text editor Ctrl+Z recovery, etc.
For more detailed articles on the use of the memo pattern in Java design patterns, please pay attention to the PHP Chinese website!