This story is part of the "Design Patterns" series. You can find the other stories of this series here:

You can also find all the code used through this series on GitHub.

The Memento pattern is a design pattern that allows an object to save its internal state at a particular point in time and then restore to that state later. This allows an object to be restored to a previous state without violating encapsulation or exposing its internal structure.

Problems the Memento Pattern can Solve

Imagine we're building a text editor that allows users to undo and redo text changes.

We can implement it in a naive way by coupling the text editor and the text to save and restore changes.

class TextEditor:
    def __init__(self):
        self.text = ""
        self.text_history = []
        self.history_index = -1

    def set_text(self, text: str):
        self.text = text
        self.text_history.append(text)
        self.history_index = len(self.text_history) - 1

    def undo(self):
        if self.history_index > 0:
            self.history_index -= 1
            self.text = self.text_history[self.history_index]

    def redo(self):
        if self.history_index < len(self.text_history) - 1:
            self.history_index += 1
            self.text = self.text_history[self.history_index]


if __name__ == "__main__":
    text_editor = TextEditor()
    text_editor.set_text("Hello")
    text_editor.set_text("Hello World")
    text_editor.undo()
    print(text_editor.text)
    text_editor.redo()
    print(text_editor.text)

Here, the TextEditor class is directly storing the entire text history in memory, which can lead to a lot of unnecessary memory usage and make it difficult to add more advanced features such as saving and loading the text history to disk.

Also, the TextEditor class is exposing its internal implementation of keeping the text history.

Solution

The Memento pattern can be used to solve this problem by creating a Memento class that stores the state of the text at a particular point in time and a Caretaker class that is responsible for creating and restoring mementos.

The text editor then has a reference to the Caretaker instance and uses its methods to create and restore mementos. This allows the text editor to save and restore the state of the text without storing the entire text history in memory.

class TextEditorMemento:
    def __init__(self, text: str):
        self.text = text


class TextEditor:
    def __init__(self):
        self.text = ""
        self.caretaker = CareTaker()
        self.caretaker.add_memento(TextEditorMemento(self.text))
        self.history_index = 0

    def set_text(self, text: str):
        self.text = text
        self.caretaker.add_memento(TextEditorMemento(self.text))
        self.history_index = self.caretaker.get_history_index()

    def undo(self):
        if self.history_index > 0:
            self.history_index -= 1
            self.text = self.caretaker.get_memento(self.history_index).text

    def redo(self):
        if self.history_index < self.caretaker.get_history_index():
            self.history_index += 1
            self.text = self.caretaker.get_memento(self.history_index).text


class CareTaker:
    def __init__(self):
        self.mementos = []
        self.history_index = -1

    def add_memento(self, memento: TextEditorMemento):
        self.mementos.append(memento)
        self.history_index = len(self.mementos) - 1

    def get_memento(self, index: int) -> TextEditorMemento:
        return self.mementos[index]

    def get_history_index(self) -> int:
        return self.history_index


if __name__ == "__main__":
    text_editor = TextEditor()
    text_editor.set_text("Hello")
    text_editor.set_text("Hello World")
    text_editor.undo()
    print(text_editor.text)
    text_editor.redo()
    print(text_editor.text)

This code maybe is a bit hard to understand without knowing the pattern, so I'll try to explain it in detail. We now have 3 classes:

  • TextEditorMemento: This class is responsible for storing the state of the text at a particular point in time. It has a single attribute 'text' that stores the text.
  • TextEditor: This is the class that needs to save and restore its state. It has a reference to the CareTaker class and uses it to create and restore mementos. The class has an attribute text that keeps the current text and history_index that keeps track of the current history state. It also has methods set_text, undo, and redo which change the text and keep track of the history.
  • CareTaker: This class is responsible for creating and restoring mementos. It has a list of mementos mementos, and an attribute history_index that keeps track of the current history state. It has methods add_memento that adds a new memento to the list, get_memento that retrieves a memento from the list and get_history_index that returns the current history state.

The TextEditor class uses the CareTakerclass to save and restore its state, by calling the methods add_memento and get_memento of the CareTakerclass. Each time the text is changed by calling the set_text method of the TextEditor class, a new memento is created using the current text and added to the mementos list in the CareTaker class and the history index is updated.

When the undo method is called, the text is restored to the previous state by retrieving the memento from the mementos list and updating the text attribute of the TextEditor class and updating the history_index accordingly. Similarly, when the redo method is called, the text is restored to the next state by retrieving the memento from the mementos list and updating the text attribute of the TextEditor class and updating the history_index accordingly.

By using the Memento pattern, the TextEditorclass can save and restore its state without directly storing the entire text history in memory, making the solution more memory efficient and easy to extend.

Applications

  • Saving and restoring the internal state of an object without violating encapsulation or exposing its internal structure.
  • Implementing undo/redo functionality in a system.
  • Storing the history of an object's state for later use.

Advantages

  • Allows an object to be restored to a previous state without violating encapsulation or exposing its internal structure.
  • Simplifies the implementation of undo/redo functionality.
  • Makes it easier to save and restore the state of an object.
  • Reduces memory usage by saving only the necessary state instead of keeping the entire history of the object.

Drawbacks

Except for the fact that the Memento pattern adds complexity to the system, I don't really see any drawback. And it doesn't really add complexity to the system because implementing undo/redo functionality without it is much harder.

Final Note

The Memento pattern is not so easy to understand. And it serves a specific purpose, so you will rarely come across it. But it's important to know, so when you'll need to implement a undo/redo functionality, you know the Memento pattern is an efficient and elegant solution.

To explore the other stories of this story, click below!

To explore more of my Python stories, click here! You can also access all my content by checking this page.

If you want to be notified every time I publish a new story, subscribe to me via email by clicking here!

If you're not subscribed to medium yet and wish to support me or get access to all my stories, you can use my link: