Memento pattern in Java and Python

Behavioral Design Patterns provide solution for the better interaction between objects and how to provide lose coupling and flexibility to extend easily. Following design patterns come under this category.

Memento pattern is about capturing and storing the current state of an object in a manner that it can be restored later on in a smooth manner.

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 an Object within the Caretaker.

The figure below shows a UML class diagram for the Memento pattern

dp_memento.png
  • Memento. Stores internal state of the Originator object. The state can include any number of state variables. The Memento must have two interfaces, an interface to the caretaker. This interface must not allow any operations or any access to internal state stored by the memento and thus honors encapsulation. The other interface is to the originator and allows the originator to access any state variables necessary to for the originator to restore previous state.
  • Originator. Creates a memento object capturing the originators internal state. Use the memento object to restore its previous state.
  • Caretaker. Responsible for keeping the memento. The memento is opaque to the caretaker, and the caretaker must not operate on it.

When to use the Memento pattern

  • A snapshot of (some portion of) an object’s state must be saved so that it can be restored to that state later.
  • A direct interface to obtaining the state would expose implementation details and break the object’s encapsulation.

One of the best real life examples is the text editors where we can save its 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.

Java

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 undo(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.

public class FileWriterCaretaker {
    private Object obj;

    public void save(FileWriterUtil fileWriter){
        this.obj = fileWriter.save();
    }

    public void undo(FileWriterUtil fileWriter){
        fileWriter.undo(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.

Lets write a simple test program that will use our memento pattern 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);

        //checking file content again
        System.out.println(fileWriter+"\n\n");
    }
}

Python 3

class Memento:
    def __init__(self, file, content):
        self.file = file
        self.content = content


class FileWriterUtil:
    def __init__(self, file):
        self.file = file
        self.content = ""

    def write(self, str):
        self.content += str

    def save(self):
        return Memento(self.file, self.content)

    def undo(self, memento):
        self.file = memento.file
        self.content = memento.content

class FileWriterCaretaker:
    def save(self, writer):
        self.obj = writer.save()

    def undo(self, writer):
        writer.undo(self.obj)


if __name__ == '__main__':
    caretaker = FileWriterCaretaker()

    writer = FileWriterUtil("data.txt")
    writer.write("First Set of Data\n")
    print(writer.content + "\n\n")

    # lets save the file
    caretaker.save(writer)
    # now write something else
    writer.write("Second Set of Data\n")

    # checking file contents
    print(writer.content + "\n\n")

    # lets undo to last save
    caretaker.undo(writer)

    # checking file content again
    print(writer.content + "\n\n")
comments powered by Disqus