This chapter covers the memento pattern.
GoF Definition
Without violating encapsulation, capture and externalize an object’s internal state so that the object can be restored to this state later.
Concept
In your application, you may need to support “undo” operations. To achieve this, you need to record the internal state of an object. So, you must save this state information in a place that can be referred again to revert back the old state of the object. But in general, objects encapsulate their states, and those states are inaccessible to the outer world. So, if you expose the state information, then you violate encapsulation.
The dictionary meaning of memento is reminder (of past events). So, you can guess that using this pattern, you can restore an object to its previous state, but it ensures that you achieve your goal without violating the encapsulation.
Real-World Example
A classic example in this category is noticed in a finite state machine. It is a mathematical model, but one of its simple applications can be found in a turnstile. It has rotating arms, which initially are locked. If you are allowed to pass through it (for example, when you insert coins or when a security person allows you to go through a security check), the locks are opened. Once you pass through, the turnstile returns to the locked state again.
Computer-World Example
In a drawing application, you may need to revert back to a prior state.
Note
You notice a similar pattern when you consider the JTextField class, which extends the javax.swing.text.JTextComponent abstract class and provides an undo support mechanism. Here javax.swing.undo.UndoManager can act as a caretaker, an implementation of javax.swing.undo. UndoableEdit can act like a memento, and an implementation of javax.swing.text.Document can act like an originator. You will learn about the terms originator, caretaker, and memento shortly. Also, java.io.Serializable is often called an example of a memento but although you can serialize a memento object, it is not a mandatory requirement for the memento design pattern.
Illustration
Go through the code and follow the comments for your ready reference. In this example, three objects are involved: a memento, an originator, and a caretaker. (These names are very common, so I have kept the same naming convention in our implementation.)
The originator object has an internal state. A client can set a state in it. A memento object may store as much or as little of the originator’s state, at the originator’s discretion. When a caretaker wants to record the state of the originator, it requests the current state from it. So, it first asks the originator for a memento object.
In the following example, the caretaker object confirms the save operation by displaying a console message. Suppose that the client makes some changes and then wants to revert back to the previous state. Since the originator object’s state is already changed, to roll back to the previous state requires help from the caretaker object, which saved the state earlier. The caretaker object returns the memento object (with the previous state) to the originator. The memento object itself is an opaque object (one which the caretaker is not allowed to make any change to, and ideally, only the originator, who created the memento can access the memento’s internal state).
So, you can conclude that caretaker has a narrow view/interface to the memento because it can only pass it to other objects. In contrast, the originator sees the wide interface because it can access the data necessary to return to a previous state.
Class Diagram
Package Explorer View
Implementation
Output
Note
If you deal with a state that is a mutable reference type, you may need to do a deep copy to store the state inside the Memento object.
Q&A Session
- 1.
I can restore the previous snapshot/restore point. But in a real-life scenario, I may have multiple restore points. How can you implement that using this design pattern?
You can use an ArrayList in such a case. Consider the following program.
The Originator class and Memento class are same as before, so I am presenting the modified Caretaker class only. I am using the following line of code in the upcoming implementation.
Modified Caretaker Class
Modified Output
Analysis
You can just go back to the previous restore point.
You can go back to your specified restore point.
You can revert back to all restore points.
- 2.
In many applications, I notice that the memento class is presented as an inner class of Originator. Why are you not following that approach?
The memento design pattern can be implemented in many different ways (for example, using package-private visibility or using object serialization techniques). But in each case, if you analyze the key aim, you find that once the memento instance is created by an originator, no one else besides its creator is allowed to access the internal state (this includes the caretaker/client). A caretaker’s job is to store the memento instance (restore points in our example) and supply them back when you are in need. So, there is no harm if your memento class is public. You can just block the public setter method for the memento. I believe that it is sufficient enough.
- 3.
But you are still using the getter method getStateId(). Does it not violate the encapsulation?
There is a lot of discussion and debate around this area—whether you should use getter/setter or not, particularly when you consider encapsulation. I believe that it depends on the amount of strictness that you want to impose. For example, if you just provide getter/setters for all fields without any reason, that is surely a bad design. The same thing applies when you use all the public fields inside the objects. But sometimes the accessor methods are required and useful. In this book, my aim is to encourage you learn design patterns with simple examples. If I need to consider each and every minute detail such as this, you may lose interest. So, in these examples, I show a simple way to promote encapsulation using the memento pattern. But, if you want to be stricter, you may prefer to implement the memento class as an inner class of the originator and modify the initial implementation, like in the following.package jdp2e.memento.questions_answers;/*The 'Originator' classWikiPedia notes( for your reference):Make an object (originator) itself responsible for:1.Saving its internal state to a(memento) object and2.Restoring to a previous state from a(memento) object.3.Only the originator that created a memento is allowed to access it.*/class Originator{private int stateId;Memento myMemento;public Originator(){this.stateId = 0;System.out.println(" Originator is created with state id : "+ stateId);}public int getStateId(){return stateId;}public void setStateId(int stateId){System.out.println(" Setting the state id of the originator to : "+ stateId);this.stateId= stateId;}//Saving its internal state to a(memento) objectpublic Memento saveMemento(){System.out.println(" Saving originator's current state id. ");//Create memento with the current state and return it.return new Memento(this.stateId);}//Restoring to a previous state from a(memento) object.public void revertMemento(Memento previousMemento){System.out.println(" Restoring to state id..."+ previousMemento.getStateId());this.stateId = previousMemento.getStateId();System.out.println(" Current state id of originator : "+ stateId);}//A memento class implemented as an inner class of Originatorstatic class Memento{private int stateId;public Memento(int stateId){this.stateId = stateId;}//Only outer class can access nowpublic int getStateId() {return stateId;}/*This class does not have thesetter method.We need to use this classto get the state of the object only.*//*public void setState(String state) {this.state = state;}*/}}/*The 'Caretaker' class.WikiPedia notes( for your reference):1.A client (caretaker) can request a memento from the originatorto save the internal state of the originator and2.Pass a memento back to the originator (to restore to a previous state)This enables to save and restore the internal state of an originator without violating its encapsulation.*/public class MementoAsInnerClassExample {public static void main(String[] args) {System.out.println("***Memento Pattern Demo*** ");//Originator is initialized with a stateOriginator originatorObject = new Originator();Originator.Memento mementoObject;originatorObject.setStateId(1);// A client (caretaker) can request a memento from the originator//to save the internal state of the originatormementoObject=originatorObject.saveMemento();System.out.println(" Snapshot #1: Originator's current state id is saved in caretaker.");//A client (or caretaker) cannot set/modify the memento's state//Changing the state id of OriginatororiginatorObject.setStateId(2);mementoObject=originatorObject.saveMemento();System.out.println(" Snapshot #2: Originator's current state id is saved in caretaker.");//Changing the state id of Originator again.originatorObject.setStateId(3);//Reverting back to previous state id.originatorObject.revertMemento(mementoObject);}} - 4.What are the key advantages of using a memento design pattern?
The biggest advantage is that you can always discard the unwanted changes and restore it to an intended or stable state.
You do not compromise the encapsulation associated with the key objects that are participating in this model.
Maintains high cohesion.
Provides an easy recovery technique.
- 5.What are key challenges associated with a memento design pattern?
A high number of mementos require more storage. At the same time, they put additional burdens on a caretaker.
The preceding point increases maintenance costs in parallel.
You cannot ignore the time to save these states. The additional time to save the states decreases the overall performance of the system.
- 6.
In these implementations, if you make the originator’s state public, then our clients also could directly access the states. Is this correct?
Yes. But you should not try to break the encapsulation. Notice the GoF definition that begins “without violating encapsulation…”
- 7.
In these implementations, the memento class does not have a public setter method. What is the reason behind this?
Go through the answer of question 2 again. And read the comment in the code that says, “Only the originator that created a memento is allowed to access it.” So, if you do not provide a public setter method for your memento class, the caretaker or client cannot modify the memento instances that are created by an originator.
- 8.In these implementations, you could ignore the getter method of the memento by using package-private visibility for stateId. For example, you could code memento class like the following.class Memento{//private int stateId;int stateId;//←-Change is herepublic Memento(int stateId){this.stateId = stateId;}public int getStateId() {return stateId;}/*This class does not have thesetter method.We need to use this classto get the state of the object only.*//*public void setState(String state) {this.state = state;}*/}And then you can use the following line.//System.out.println(" Restoring to state id..."+ previousMemento.getStateId());System.out.println(" Restoring to state id..."+ previousMemento.stateId);//←The change is shown in bold
Is this correct?
Yes. In many application, other classes (except originator) cannot even read the memento’s state. When you use package-private visibility, you do not need any accessor method. In other words, you are simply using the default modifier in this case.
So, this kind of visibility is slightly more open than private visibility and other classes in the same package can access a class member. So, in this case, the intended classes need to be placed inside the same package. At the same time, you need to accept that all other classes inside the same package will have the direct access to this state. So, you need to be careful enough when you place the classes in your special package.
- 9.
I am confused. To support undo operations, which pattern should I prefer—memento or command?
The GoF told us that these are related patterns. It primarily depends on how you want to handle the situation. For example, suppose that you are adding 10 to an integer. After this addition, you want to undo the operation by doing the reverse operation (i.e., 50 + 10 = 60, so to go back, you do 60 –10 = 50). In this type of operation, we do not need to store the previous state.
But consider a situation where you need to store the state of your objects prior to the operations. In this case, memento is your savior. So, in a paint application, you can avoid the cost of undoing a paint operation. You can store the list of objects prior to executing the commands. This stored list can be treated as a memento in this case. You can keep this list with the associated commands. I suggest that you read the nice online article at www.developer.com/design/article.php/3720566/Working-With-Design-Patterns-Memento.htm .
So, an application can use both patterns to support undo operations.
Finally, you must remember that storing a memento object is mandatory in a memento pattern, so that you can roll back to a previous state; but in a command pattern, it is not necessary to store the commands. Once you execute a command, its job is done. If you do not support “undo” operations, you may not be interested in storing these commands at all.
- 10.
You talked about deep copy after the first implementation. Why do I need that?
In Chapter 2 (the prototype pattern), I discussed shallow copy and deep copy. You can refer to this discussion for your reference. To answer your question, let’s analyze what is special about deep copy with a simple example. Consider the following example.
Shallow Copy vs. Deep Copy in Java
You clone with the clone() method in Java, but at the same time, you need to implement the Cloneable interface (which is a marker interface) because the Java objects that implement this Cloneable interface are only eligible for cloning. The default version of clone()creates a shallow copy. To create the deep copy, you need to override the clone() method .
Key Characteristics of the Following Program
In the following example, you have two classes: Employee and EmpAddress.
The Employee class has three fields: id, name, and EmpAddress. So, you may notice that to form an Employee object, you need to pass an EmpAddress object also. So, in the following example, you will notice the line of code:
Employee emp=new Employee(1,"John",initialAddress);
EmpAddress has only a field called address, which is a String datatype.
In the client code, you create an Employee object emp and then you create another object, empClone, through cloning. So, you will notice the line of code as follows:
Employee empClone=(Employee)emp.clone();
Then you change the field values of the emp object. But as a side effect of this change, the address of empClone object also changes, but this is not wanted.
Implementation
Output
Analysis
Modified Output
Analysis
Note
You saw the theoretical parts of a shallow copy and a deep copy in the “Q&A Session” of Chapter 2.