3.8 - Memento Pattern : Behavioral Design Patterns

The Memento Pattern is a behavioral design pattern that allows an object to save its state and restore it later without exposing its internal structure. This pattern is useful when you need to implement undo/redo functionality or save checkpoints in an application.

In simpler terms, the Memento Pattern captures and stores the current state of an object so that it can be reverted to that state later. The core concept is to preserve an object's state without breaking encapsulation.

Key Components of the Memento Pattern:

  1. Memento: The memento object stores the internal state of the originator. It doesn't allow any operations or modifications to the state stored, ensuring immutability.

  2. Originator: The originator is the object whose state needs to be saved. It can create mementos and use them to restore its previous state.

  3. CareTaker: The caretaker is responsible for storing and managing mementos. It doesn’t modify or operate on the memento’s contents.

Code Example

Let’s walk through an example of the Memento Pattern using modified class names and function names for clarity.

1. Memento Class

The Snapshot class stores the internal state of the Document. It provides a way to retrieve the stored state but doesn’t allow changes to the state.

// Memento class
class Snapshot {
    private String content;

    public Snapshot(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }
}

2. Originator Class

The Document class (acting as the originator) has a content field representing the current state. It can save the current content to a snapshot and restore content from a snapshot.

// Originator class
class Document {
    private String content;

    public void setContent(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }

    // Saves the current state into a snapshot (memento)
    public Snapshot createSnapshot() {
        return new Snapshot(content);
    }

    // Restores the content from a snapshot (memento)
    public void restoreFromSnapshot(Snapshot snapshot) {
        content = snapshot.getContent();
    }
}

3. Client Code

The MementoPatternDemo class demonstrates saving and restoring the state using the Memento Pattern.

// Client
public class MementoPatternDemo {
    public static void main(String[] args) {
        Document document = new Document();

        // Set initial state
        document.setContent("Initial Draft");

        // Save the current state
        Snapshot snapshot = document.createSnapshot();

        // Modify the state
        document.setContent("Updated Draft");
        System.out.println("Current Content: " + document.getContent());  // Output: Updated Draft

        // Restore to the previous state
        document.restoreFromSnapshot(snapshot);
        System.out.println("Restored Content: " + document.getContent());  // Output: Initial Draft
    }
}

Explanation:

  1. Snapshot (Memento): This class represents the memento object that stores the content of the Document. Once created, the snapshot cannot be modified to ensure immutability.

  2. Document (Originator): This class represents the originator that has a state (content). It can create snapshots to save its current state and restore from snapshots when needed.

  3. MementoPatternDemo (Client): This class shows how the Memento Pattern can be used to store and restore the state of the document. It demonstrates saving the current state using a snapshot, modifying the state, and then restoring it from the saved snapshot.

Benefits of the Memento Pattern:

  1. Encapsulation Preservation: The memento pattern keeps the internal details of the object hidden from external classes. Only the originator has access to the internal state and can save or restore it.

  2. Undo/Redo Functionality: It’s ideal for implementing undo/redo functionality by storing multiple states and reverting to the previous states.

  3. Simplifies State Management: The pattern provides a clean way to manage and restore object states, reducing the complexity of handling state transitions manually.

Drawbacks of the Memento Pattern:

  1. Memory Usage: Storing multiple mementos can consume a significant amount of memory, especially if the state is large or there are frequent changes.

  2. Complexity with Many States: Managing a large number of mementos may become challenging if you need to handle complex state changes and history tracking.

  3. Memento Visibility: The memento object needs to be carefully managed so that it is not altered by external objects, which could break the integrity of the saved state.

Real-World Use Cases:

  • Text Editors: Many text editors implement the undo/redo functionality using the Memento Pattern. When the user makes a change, the editor saves a snapshot of the document before the change. The user can then undo the changes and revert to a previous state.

  • Game State Saving: In video games, the Memento Pattern can be used to save the state of the game (e.g., player position, score) and allow the player to revert to that state later.

  • Database Transactions: Some database systems use the Memento Pattern to store the state of the database before making a transaction. In case of a failure, the system can roll back to the previous state.

Enhanced Example: Caretaker for Managing Snapshots

In more complex scenarios, a Caretaker class may be introduced to manage multiple snapshots. It can store a list of mementos (snapshots) and restore to a specific point.

// Caretaker class
class Caretaker {
    private List<Snapshot> snapshotHistory = new ArrayList<>();

    public void saveSnapshot(Snapshot snapshot) {
        snapshotHistory.add(snapshot);
    }

    public Snapshot getSnapshot(int index) {
        return snapshotHistory.get(index);
    }
}

public class MementoPatternWithCaretakerDemo {
    public static void main(String[] args) {
        Document document = new Document();
        Caretaker caretaker = new Caretaker();

        // Set and save initial state
        document.setContent("First Draft");
        caretaker.saveSnapshot(document.createSnapshot());

        // Update and save another state
        document.setContent("Second Draft");
        caretaker.saveSnapshot(document.createSnapshot());

        // Modify content further
        document.setContent("Final Draft");
        System.out.println("Current Content: " + document.getContent());  // Output: Final Draft

        // Restore to the second state
        document.restoreFromSnapshot(caretaker.getSnapshot(1));
        System.out.println("Restored Content: " + document.getContent());  // Output: Second Draft
    }
}

In this enhanced version:

  • Caretaker manages multiple mementos, allowing you to go back to any previous state.

  • The Document still handles saving and restoring states using snapshots.

Conclusion:

The Memento Pattern is a powerful design pattern for saving and restoring the state of objects while preserving encapsulation. By using mementos (snapshots), it allows for implementing undo/redo functionality and state rollback in applications. While it introduces additional memory usage due to saved states, it simplifies state management in scenarios where state transitions are frequent and require restoration.

2
Subscribe to my newsletter

Read articles from Venu Madhav Emmadi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Venu Madhav Emmadi
Venu Madhav Emmadi