Memento Pattern
Category
Behavioral Design Pattern
Overview
The Memento Pattern is a behavioral design pattern that provides the ability to restore an object to its previous state without exposing its implementation details. This is achieved by creating a memento object that captures and stores the internal state of another object (referred to as the originator).
This pattern is particularly useful when:
You need to implement an undo/redo mechanism.
You want to isolate and protect the internal state of an object from direct access by other parts of the system.
Key Characteristics
State Preservation:
Enables saving and restoring an object’s internal state at a specific point in time.
Encapsulation:
Protects the internal state of the originator by encapsulating it within a memento object, which can only be accessed by the originator.
Undo/Redo Operations:
Commonly used in systems requiring undo/redo functionality, such as text editors or games.
Role Separation:
Defines three key participants:
Originator: The object whose state needs saving.
Memento: The object storing the saved state.
Caretaker: Manages mementos and interacts with the originator to save or restore states.
Non-Intrusive:
The memento object does not interfere with the originator’s behavior; it merely stores state data.
UML Diagram
The UML diagram below illustrates the relationships between the Originator, Memento, and Caretaker in the Memento Pattern.
Implementation Walkthrough
Participants
Originator:
The main object whose state needs to be saved and restored.
Creates and restores mementos.
Memento:
A storage object that holds the internal state of the originator.
Provides controlled access to the stored state.
Caretaker:
Manages mementos and coordinates the saving and restoring of the originator’s state.
Example: Undo/Redo in a Text Editor
Originator: TextEditor
/**
* @brief The Originator that maintains the state to be saved or restored.
*/
public class TextEditor {
private String content;
public void setContent(String content) {
this.content = content;
}
public String getContent() {
return content;
}
/**
* @brief Creates a memento containing the current state.
* @return A new Memento instance.
*/
public Memento save() {
return new Memento(content);
}
/**
* @brief Restores the state from a given memento.
* @param memento The memento containing the saved state.
*/
public void restore(Memento memento) {
this.content = memento.getContent();
}
}
Memento
/**
* @brief The Memento storing the state of the Originator.
*/
public class Memento {
private final String content;
public Memento(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
Caretaker: HistoryManager
/**
* @brief The Caretaker that manages the memento lifecycle.
*/
import java.util.Stack;
public class HistoryManager {
private Stack<Memento> history = new Stack<>();
public void save(Memento memento) {
history.push(memento);
}
public Memento undo() {
if (!history.isEmpty()) {
return history.pop();
}
return null;
}
}
Client Code
/**
* @brief Demonstrates the Memento Pattern with undo functionality.
*/
public class MementoPatternDemo {
public static void main(String[] args) {
TextEditor editor = new TextEditor();
HistoryManager history = new HistoryManager();
editor.setContent("Version 1");
history.save(editor.save());
editor.setContent("Version 2");
history.save(editor.save());
editor.setContent("Version 3");
System.out.println("Current content: " + editor.getContent());
editor.restore(history.undo());
System.out.println("After undo: " + editor.getContent());
editor.restore(history.undo());
System.out.println("After another undo: " + editor.getContent());
}
}
Output
Current content: Version 3
After undo: Version 2
After another undo: Version 1
Applications
When to Use the Memento Pattern
When you need to implement undo/redo functionality.
When you want to save and restore the state of an object without exposing its implementation details.
When you want to minimize coupling between the state-saving logic and the object itself.
Common Use Cases
Text Editors:
Undo/redo functionality for editing text.
Games:
Saving and loading game progress.
Configuration Systems:
Restoring system settings or configurations.
Advantages and Disadvantages
Advantages
Encapsulation:
Protects the originator’s internal state from external modification.
Flexibility:
Allows saving and restoring an object’s state at runtime.
Scalability:
The caretaker can manage multiple mementos, enabling complex state transitions.
Disadvantages
Memory Overhead:
Maintaining multiple mementos can consume significant memory.
Complexity:
Managing mementos and ensuring proper lifecycle management can be challenging.
Limited Access:
If not designed properly, the caretaker may inadvertently expose the originator’s state.
Key Takeaways
The Memento Pattern provides a structured way to save and restore the state of objects, enabling powerful features like undo/redo and checkpointing. While it introduces additional components and complexity, its benefits in preserving state and encapsulation make it an essential pattern in behavioral design.