Introduction

The Memento pattern is a behavioral design pattern that allows an object to save and restore its state without revealing its implementation details. This pattern is particularly useful for implementing undo mechanisms in applications.

Key Concepts

  • Originator: The object whose state needs to be saved and restored.
  • Memento: The object that stores the state of the Originator.
  • Caretaker: The object that requests the save and restore operations from the Originator.

Structure

The Memento pattern involves three main components:

  1. Originator: Creates a memento containing a snapshot of its current internal state and uses the memento to restore its internal state.
  2. Memento: Stores the internal state of the Originator object. The Memento protects against access by objects other than the Originator.
  3. Caretaker: Responsible for keeping the memento. The Caretaker never operates on or examines the contents of a memento.

UML Diagram

+----------------+       +----------------+       +----------------+
|   Originator   |       |    Memento     |       |   Caretaker    |
+----------------+       +----------------+       +----------------+
| - state        |       | - state        |       | - memento      |
+----------------+       +----------------+       +----------------+
| + createMemento()      | + getState()   |       | + saveState()  |
| + setMemento(memento)  | + setState()   |       | + restoreState()|
+----------------+       +----------------+       +----------------+

Example

Let's implement a simple example in Python to demonstrate the Memento pattern. We will create a text editor that can save and restore its state.

Code Implementation

Originator

class TextEditor:
    def __init__(self):
        self._content = ""

    def type(self, words):
        self._content += words

    def get_content(self):
        return self._content

    def save(self):
        return Memento(self._content)

    def restore(self, memento):
        self._content = memento.get_content()

Memento

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

    def get_content(self):
        return self._content

Caretaker

class Caretaker:
    def __init__(self, editor):
        self._editor = editor
        self._history = []

    def save(self):
        self._history.append(self._editor.save())

    def undo(self):
        if not self._history:
            return
        memento = self._history.pop()
        self._editor.restore(memento)

Usage

if __name__ == "__main__":
    editor = TextEditor()
    caretaker = Caretaker(editor)

    editor.type("Hello, ")
    caretaker.save()

    editor.type("world!")
    caretaker.save()

    editor.type(" How are you?")
    print("Current content:", editor.get_content())

    caretaker.undo()
    print("After undo:", editor.get_content())

    caretaker.undo()
    print("After second undo:", editor.get_content())

Output

Current content: Hello, world! How are you?
After undo: Hello, world!
After second undo: Hello,

Practical Exercises

Exercise 1: Implement a Calculator with Undo Functionality

Task: Implement a simple calculator that supports addition and subtraction. The calculator should be able to save its state and undo the last operation.

Solution:

Calculator (Originator)

class Calculator:
    def __init__(self):
        self._value = 0

    def add(self, number):
        self._value += number

    def subtract(self, number):
        self._value -= number

    def get_value(self):
        return self._value

    def save(self):
        return Memento(self._value)

    def restore(self, memento):
        self._value = memento.get_value()

Memento

class Memento:
    def __init__(self, value):
        self._value = value

    def get_value(self):
        return self._value

Caretaker

class Caretaker:
    def __init__(self, calculator):
        self._calculator = calculator
        self._history = []

    def save(self):
        self._history.append(self._calculator.save())

    def undo(self):
        if not self._history:
            return
        memento = self._history.pop()
        self._calculator.restore(memento)

Usage

if __name__ == "__main__":
    calculator = Calculator()
    caretaker = Caretaker(calculator)

    calculator.add(10)
    caretaker.save()

    calculator.add(5)
    caretaker.save()

    calculator.subtract(3)
    print("Current value:", calculator.get_value())

    caretaker.undo()
    print("After undo:", calculator.get_value())

    caretaker.undo()
    print("After second undo:", calculator.get_value())

Output

Current value: 12
After undo: 15
After second undo: 10

Common Mistakes and Tips

  • Not Saving State Frequently: Ensure you save the state at appropriate times to avoid losing important changes.
  • Excessive Memory Usage: Be mindful of the memory usage when saving states, especially for large objects.
  • Restoring Incorrect State: Always ensure that the memento being restored is valid and corresponds to the correct state.

Conclusion

The Memento pattern is a powerful tool for managing the state of an object, especially when implementing undo functionality. By separating the state-saving logic from the main business logic, it promotes cleaner and more maintainable code. In the next section, we will explore the Observer pattern, which allows objects to notify other objects about changes in their state.

© Copyright 2024. All rights reserved