Memento
#design patterns #clean codeEver used an image editing software? We opened an image but it’s too sharp and the light is not good. We make some changes and still not satisfied, we make few more changes only to realize that the previous changes were better than these new! We want to restore to previous changes. If we were using a drawing sheet and paint brush, this task would be very difficult; perhaps impossible. But with softwares, that’s not a problem. We can simply select the state from history which we like the most and start working on that and continue as if we never made these new changes. Undo is one of most ingenious tool developed. Let’s see how we can create our own undo tool in this post.
Memento or sometime also called as Token is described as follows in Gang of Four:
Without violating encapsulation, capture and externalize an object’s internal state so that the object can be restored to this state later.
Let’s see the most important part of this definition first:
Without violating encapsulation, capture and externalize an object’s internal state so that the object can be restored to this state later.
Basically, we are storing an object’s state to restore later.
How? Externally, without violating encapsulation.
That was simple. But how to do it? Let’s take a simpler example of a Word Processor. What we need to track is the text entered by user and restore it later when the user clicks undo. In other words, we have to capture the text such that it can be restored later.
So say we have some object that will store the text in the document. It’s state will be the text on the document. Let’s call this object - originator. Then we need a mechanism to represent the snapshot. The way we take our photograph to preserve the moment, we need a snapshot of the text at the very instant. Let’s call the snapshot - Memento. Now we need someone, who will store these snapshots and give them to us when asked. Like a caretaker but of snapshots. Let’s call it Caretaker.
So we have 3 characters in our play - The originator, the memento and the caretaker.
Originator will request to take snapshot or restore them. Memento is the snapshot and caretaker is going to store them for later use.
Implementing it
Implementing this may sound tricky but is in fact very easy.
At the very basic level, the state of text can simple be represented like this:
class Text {
String currentTextInBuffer;
}
and memento can be represented like this:
class TextMemento {
String state;
/* getters and setters ommitted */
}
Now let’s look at the part that says - without violating encapsulation. We must not create a separate object that will
clone or get internal state of Text
object. Instead this responsibility is of the originator in our case Text
object to provide appropriate methods for this. So let’s fix this.
class Text {
String currentTextInBuffer;
public TextMemento getState() {
TextMemento m = new TextMemento();
m.setState(currentTextInBuffer);
return m;
}
public void setState(TextMemento m) {
this.currentTextInBuffer = m.getState();
}
}
That’s good, but now we need to store some states. That too externally. Let’s create a caretaker object that is going to do this.
public class CareTaker {
private List<TextMemento> states = new LinkedList<TextMemento>();
public void saveState(TextMemento m) {
states.add(m);
}
public TextMemento getState(int stateIndex) {
return states.get(stateIndex);
}
}
We have used LinkedList
to save the state. We could use Stack
or Queue
depending on the requirement. We could also
provide some helper methods for example to return a state nth
iteration previous from current state.
public class CareTaker {
/* previous implementation here */
/*
* Returns the previous state
*/
public TextMemento getPreviousState() {
return state.get(state.size() - 1);
}
/*
* Returns nth previous state
*/
public TextMemento getNthPreviousState(int n) {
return state.get(state.size() - n);
}
}
That’s it! We are done. With these 3 objects, we can travel time. We can save changes when required and restore them when required!
Points to consider
Using Memento might be expensive in terms of storage size. Therefore we must use it carefully and try to keep as less internal information required as possible. We might have to keep a fixed size of the list and keep deleting older entries from the list.
Not violating encapsulation is very important and the state of the object should never be in wrong hands. That is, the object should return a Memento when asked for.
Examples
When ever we have incremental progress of some object, we can use Memento to track and restore it!
For example user filling a very complex form or the email we type to saving and then loading a game we were playing!
That was Memento pattern, Bye Bye till next time!