Memento Pattern in Java – Restore Object State

Illustration for Memento Pattern in Java – Restore Object State
By Last updated:

Introduction

The Memento Pattern is a behavioral design pattern that allows an object to save and restore its state without exposing its internal structure. Think of it like a "snapshot" feature—useful in undo mechanisms, game save points, or restoring configurations.

This pattern is extremely useful when you want to provide undo/redo capabilities or maintain the history of changes.


Core Intent and Participants

Intent: Capture and externalize an object's internal state so that it can be restored later without violating encapsulation.

Participants

  • Originator – The object whose state needs to be saved.
  • Memento – Stores the internal state of the Originator.
  • Caretaker – Manages the history of Mementos but does not modify or inspect their content.

UML-style Representation

+----------------+        +---------------+        +----------------+
|  Caretaker     |<------>|   Memento     |<------>|   Originator   |
+----------------+        +---------------+        +----------------+
        |                            |                       |
   Save/Retrieve             Stores State             Creates/Restores

Real-World Use Cases

  • Undo/Redo operations in editors
  • Saving checkpoints in games
  • Backup/restore of configuration settings
  • Transaction history in financial systems

Java Implementation Strategy

Step-by-Step Example

1. Memento Class

public class Memento {
    private final String state;

    public Memento(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
}

2. Originator Class

public class Originator {
    private String state;

    public void setState(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }

    public Memento saveToMemento() {
        return new Memento(state);
    }

    public void restoreFromMemento(Memento memento) {
        this.state = memento.getState();
    }
}

3. Caretaker Class

import java.util.Stack;

public class Caretaker {
    private final Stack<Memento> history = new Stack<>();

    public void saveState(Originator originator) {
        history.push(originator.saveToMemento());
    }

    public void undo(Originator originator) {
        if (!history.isEmpty()) {
            originator.restoreFromMemento(history.pop());
        }
    }
}

4. Demo

public class Main {
    public static void main(String[] args) {
        Originator editor = new Originator();
        Caretaker caretaker = new Caretaker();

        editor.setState("Version 1");
        caretaker.saveState(editor);

        editor.setState("Version 2");
        caretaker.saveState(editor);

        editor.setState("Version 3");
        System.out.println("Current: " + editor.getState());

        caretaker.undo(editor);
        System.out.println("Undo 1: " + editor.getState());

        caretaker.undo(editor);
        System.out.println("Undo 2: " + editor.getState());
    }
}

Pros and Cons

✅ Pros

  • Preserves encapsulation
  • Enables undo/redo functionality
  • Simplifies rollback scenarios

❌ Cons

  • Can consume lots of memory
  • Might be complex for objects with large state

Anti-patterns and Misuse

  • Storing too many snapshots leads to memory bloat.
  • Leaking implementation details via Memento accessors.
  • Overusing Memento when simpler strategies like Command pattern may suffice.

Comparison with Similar Patterns

Pattern Purpose Example Use Case
Memento Save/restore internal state Undo history
Command Encapsulate actions with history Action queue, redo stack
Prototype Clone object including internal state Object duplication

Refactoring Legacy Code

To refactor legacy systems:

  1. Identify state that users might want to undo or save.
  2. Introduce a Memento object to capture this state.
  3. Use a Caretaker to store the Mementos.

Best Practices

  • Keep Memento immutable.
  • Avoid deep copying unless necessary.
  • Use serialization if state is complex.
  • Ensure thread-safety if shared across threads.

Real-World Analogy

A game save system is a classic Memento. When you save a game, you capture the entire game state at that moment. If something goes wrong, you simply reload the save to restore that state.


Java Version Relevance

If you're using Java 14+:

  • You can use Records to simplify the Memento structure.
public record Memento(String state) {}

Conclusion & Key Takeaways

  • The Memento Pattern is great for capturing and restoring state.
  • Useful in undo, versioning, and rollback functionality.
  • Keep your design clean by decoupling the history management (Caretaker) from the core logic (Originator).

FAQ – Memento Pattern in Java

1. When should I use the Memento Pattern?

Use it when you want to implement undo/redo functionality or maintain state history.

2. Is Memento pattern part of core Java libraries?

No, but it’s easy to implement and commonly used in Java apps.

3. Can I use Java Serialization as a Memento?

Yes, but it might be overkill for small state and adds I/O overhead.

4. What are the drawbacks of the Memento pattern?

Potential memory overhead and increased complexity.

5. Should the Memento class be public?

Not necessarily. It can be private or protected to preserve encapsulation.

6. How do I handle large object graphs in Memento?

Use shallow copies or serialize only the needed parts.

7. Is the Memento Pattern thread-safe?

Only if implemented correctly. Use synchronization when shared across threads.

8. Can I use Java Record as a Memento?

Yes, especially for simple, immutable state.

9. How does this differ from the Prototype pattern?

Memento is for saving/restoring state. Prototype is for cloning objects.

10. What are real-world systems using Memento?

IDEs (undo), games (save/load), graphic editors (version control).