Capturing
wonderful Moments of Object Life by M emento Pattern
The GoF book says that the Memento pattern is designed to,
“Without violating encapsulation, capture and externalize an object’s internal
state so that the object can be restored to this state later.”
Memento pattern is one of the behavioral design pattern. Memento design pattern is used when
we want to save the state of an object so that we can restore later on. Memento
pattern is used to implement this in such a way that the saved state data of the
object is not accessible outside of the object, this protects the integrity of
saved state data.
Memento pattern is implemented with two objects – Originator and Caretaker. Originator is the object whose state needs to be saved
and restored and it uses an inner class to
save the state of Object. The inner class is called Memento and its private, so that it can’t be accessed from other
objects.
Caretaker is the
helper class that is responsible for storing and restoring the Originator’s
state through Memento object. Since Memento is private to Originator, Caretaker
can’t access it and it’s stored as a Object within the caretaker.
One of the best real
life example is the text editors where we can save it’s data anytime and use
undo to restore it to previous saved state. We will implement the same feature
and provide a utility where we can write and save contents to a File anytime
and we can restore it to last saved state. For simplicity, I will not use any
IO operations to write data into file.
package memento;
public class FileWriterUtil {
private String fileName;
private StringBuilder content;
public FileWriterUtil(String file){
this.fileName=file;
this.content=new StringBuilder();
}
@Override
public String toString(){
return this.content.toString();
}
public void write(String str){
content.append(str);
}
public Memento save(){
return new Memento(this.fileName,this.content);
}
public void undoToLastSave(Object obj){
Memento memento = (Memento) obj;
this.fileName= memento.fileName1;
this.content=memento.content1;
}
private class Memento{
private String fileName1;
private StringBuilder content1;
public
Memento(String file, StringBuilder content){
this.fileName1=file;
//notice the deep copy so that Memento
and
//FileWriterUtil content variables don't
refer to same object
this.content1=new
StringBuilder(content);
}
}
}
Notice the Memento inner class and
implementation of save and undo methods. Now we can continue to implement
Caretaker class.
package memento;
public class FileWriterCaretaker {
private Object obj;
public void save(FileWriterUtil fileWriter){
this.obj=fileWriter.save();
}
public void undo(FileWriterUtil fileWriter){
fileWriter.undoToLastSave(obj);
}
}
Notice that caretaker
object contains the saved state in the form of Object, so it can’t alter its
data and also it has no knowledge of it’s structure.
Memento Test Class
Lets write a simple
test program that will use our memento implementation.
package memento;
public class FileWriterClient {
public static void main(String[] args) {
FileWriterCaretaker caretaker = new FileWriterCaretaker();
FileWriterUtil fileWriter
= new FileWriterUtil("data.txt");
fileWriter.write("First Set of Data\n");
System.out.println(fileWriter+"\n\n");
// lets save the file
caretaker.save(fileWriter);
//now write something else
fileWriter.write("Second Set of Data\n");
//checking file contents
System.out.println(fileWriter+"\n\n");
//lets undo to last save
caretaker.undo(fileWriter);
//checking file content again
System.out.println(fileWriter+"\n\n");
}
}
Output
of above program is:
First Set of Data
First Set of Data
Second Set of Data
First Set of
Data
The pattern is simple
and easy to implement, one of the thing needs to take care is that Memento
class should be accessible only to the Originator object. Also in client
application, we should use caretaker object for saving and restoring the
originator state.
Also if Originator
object has properties that are not immutable, we should use deep copy or
cloning to avoid data integrity issue like I have used in above example. We can
use Serialization to achieve memento pattern implementation that is more
generic rather than Memento pattern where every object needs to have it’s own
Memento class implementation.
One of the drawback is
that if Originator object is very huge then Memento object size will also be
huge and use a lot of memory.
java.util.Date (the setter methods do that, Date is internally represented by a long value)
ReplyDeleteAll implementations of java.io.Serializable
All implementations of javax.faces.component.StateHolder