Memento Design Pattern

Intent

Without violating encapsulation, capture and externalize an objects internal state so that the object can be restored to this state later.
One of the best real-life example is the text editors where we can save its data anytime and use undo to restore it to previous saved state. 

Also Known As

Token

Structure


Participants

1. Memento 
  • This component stores internal state of the Originator object. The memento may store as much or as little of the originator's internal state as necessary at its originator's discretion.
  • This component protects against access by objects other than the originator. Mementos have effectively two interfaces. Caretaker sees a narrow interface to the Memento—it can only pass the memento to other objects. Originator, in contrast, sees a wide interface, one that lets it access all the data necessary to restore itself to its previous state. Ideally, only the originator that produced the memento would be permitted to access the memento's internal state.
2. Originator
  • This component creates a memento containing a snapshot of its current internal state.
  • This component uses the memento to restore its internal state.
3. Caretaker 
  • It is responsible for the memento's safekeeping.
  • It never operates on or examines the contents of a memento

Source code

Memento pattern is implemented with two objects – Originator and Caretaker. The 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 an Object within the caretaker.
Step 1: Let's create Originator Class.
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.fileName;
  this.content = memento.content;
 }
 private class Memento {
  private String fileName;
  private StringBuilder content;
  public Memento(String file, StringBuilder content) {
   this.fileName = file;
   //notice the deep copy so that Memento and FileWriterUtil
   content variables don 't refer to same object
   this.content = new StringBuilder(content);
  }
 }
}
Notice the Memento inner class and implementation of save and undo methods. Now we can continue to implement Caretaker class.
Step 2: Create Caretaker Class.
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 its structure.
Step 3: Let's test Memento design pattern.Let’s write a simple test program that will use our memento implementation.
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);
 }

Applicability

Use the Memento pattern when
  • a snapshot of an object's state must be saved so that it can be restored to that state later, and
  • a direct interface to obtaining the state would expose implementation details and break the object's encapsulation

Real world examples

Comments