The Memento Design Pattern is used to store the internal state of an object so that it can be used later to restore the object back to historical state when needed. It uses a Memento object to store it's state. The state information in the memento object is not accessible form outside of the object and thus honors encapsulation. This protects the integrity of the saved state data.
This pattern is useful when you need to implement undo functionality, maintain a history of changes, or provide a snapshot mechanism for an object. In this tutorial, we will explore the Memento Design Pattern in Java. We'll cover its structure, implementation, and use cases. Additionally, we'll discuss best practices for implementing the pattern and highlight its advantages.
Structure of the Memento Design Pattern
The Memento Design Pattern involves the following key components:- Originator : The object whose state we want to save for future use. It creates memento object capturing its internal state. It also uses previously saved memento to restore to its previous state.
- CareTaker : The object which maintains the history of the states of Originator. It provides the data store for saving and restoring internal states. It cannot read or change the data of a memento object.
- Memento : It is used to store the internal states of Originator at any moment of time. It is created and retrieved by the Originator and stored and maintained by Caretaker.
Advantages of Memento Design Pattern
- Undo/Redo Functionality : One of the primary use cases of the Memento Design Pattern is the implementation of undo and redo functionality. It allows users to revert an object's state to a previous state or move forward to a more recent state.
- Isolation of State : The Memento Design Pattern isolates the internal state of an object from the rest of the system. This encapsulation ensures that the state is not directly accessible or modifiable by external classes.
- Snapshot Mechanism : The pattern provides a snapshot mechanism, allowing the originator to capture its internal state at a specific point in time. This can be useful for creating checkpoints or implementing versioning systems.
- Maintainability : By encapsulating the state in mementos, the originator's internal implementation details remain hidden. This encapsulation contributes to the maintainability of the code, as changes to the originator's implementation do not affect the caretaker.
- Supports Transactional Behavior : Mementos can be used to implement transactional behavior, where a series of operations can be grouped, and the system can be rolled back to a previous state if any operation fails.
- Flexibility in Restoring State : The pattern provides flexibility in restoring an object's state. Different mementos can be applied to achieve specific states, allowing for granular control over the restoration process.
- Separation of Concerns : The Memento Design Pattern promotes the separation of concerns by isolating state-related functionality. This separation simplifies the design and allows each component to focus on its specific responsibilities.
- History Tracking : The caretaker can keep a history of an object's state changes by maintaining a collection of mementos. This history tracking is valuable for scenarios where a complete record of changes is needed.
When we should use Memento Pattern
- When we want to restore back an abject to its previous state. It is used heavily in GUI applications for doing undo and rollback operations.
- To maintain the atomicity of a database transaction. If a transaction failed in intermediate steps then we have to rollback all the operations performed by the transaction handler till now.
- When we want to maintain a history of states of an object.
- When we don’t want to expose the internal state of an object.
Implementation of Memento Design Pattern

public class Memento { private double temperature; private double pressure; private double volume; public Memento(double temp, double pressure, double volume){ this.temperature = temp; this.pressure = pressure; this.volume = volume; } public double getTemperature(){ return temperature; } public void printMemento(){ System.out.println("State : [ Temperature = " + temperature + ", Pressure = " + pressure + ", Volume = " + volume + "]"); } public double getPressure(){ return pressure; } public double getVolume() { return volume; } }
Originator.java
public class Originator { /* * Temperature, Pressure and Volume defines * the state of a system under observation */ private double temperature; private double pressure; private double volume; public void setState(double temp, double pressure, double volume){ this.temperature = temp; this.pressure = pressure; this.volume = volume; } public void printState(){ System.out.println("State : [ Temperature = " + temperature + ", Pressure = " + pressure + ", Volume = " + volume + "]"); } public Memento saveToMemento(){ return new Memento(temperature, pressure, volume); } public void restoreStateFromMemento(Memento m){ this.temperature = m.getTemperature(); this.pressure = m.getPressure(); this.volume = m.getVolume(); } }
CareTaker.java
import java.util.Map; import java.util.Map.Entry; import java.util.HashMap; public class CareTaker { private int counter; private Map<Integer, Memento> mementoMap = new HashMap<Integer, Memento>(); public CareTaker() { counter = 1; } public void addState(Memento m){ mementoList.put(counter, m); counter++; } public void removeState(int i) { mementoMap.remove(i); } public void printAllSavedState() { System.out.println("------ Saved States------"); for(Entry<Integer, Memento> entry : mementoMap.entrySet()){ System.out.println("State " + entry.getKey()); entry.getValue().printMemento(); } System.out.println("-------------------------"); } public Memento getState(int i){ return mementoMap.get(i); } }
MementoPatternExample.java
public class MementoPatternExample { public static void main(String args[]) { Originator originator = new Originator(); CareTaker careTaker = new CareTaker(); // Set initial state of system originator.setState(10.5, 5.4, 100.3); // Save initial state of system careTaker.addState(originator.saveToMemento()); // Change state of system originator.setState(15.5, 3.1, 105.1); originator.setState(6.2, 8.3, 99.9); originator.setState(8.4, 7.2, 111.0); // Second Check point, Save state of system again careTaker.addState(originator.saveToMemento()); // Change state of system originator.setState(12.5, 2.2, 123.4); // Print all saved states of syystem careTaker.printAllSavedState(); // Printing current state of system System.out.println("------ Current State------"); originator.printState(); // Restore state of system to initial state originator.restoreStateFromMemento(careTaker.getState(1)); // Printing current state of system after restoring System.out.println("------ State after Restoration------"); originator.printState(); } }
Output
------ Saved States------ State 1 State : [ Temperature = 10.5, Pressure = 5.4, Volume = 100.3] State 2 State : [ Temperature = 8.4, Pressure = 7.2, Volume = 111.0] ------------------------- ------ Current State------ State : [ Temperature = 12.5, Pressure = 2.2, Volume = 123.4] ------ State after Restoration------ State : [ Temperature = 10.5, Pressure = 5.4, Volume = 100.3]
Best Practices of Memento Design Pattern
- Design the memento class to be immutable. This ensures that once a memento is created, its state cannot be altered. Immutability guarantees that the memento captures a consistent snapshot of the originator's state.
- Carefully choose what state to capture in the memento. Include only the state that is necessary for restoring the originator to a consistent and meaningful state.
- Limit the visibility of the memento class. Ideally, the memento class should have package-private or private access, and access to its internals should be controlled through well-defined methods
- If your application involves versioning or rollback mechanisms, consider adding version information to your mementos. This information can help in managing different versions of an object's state.
- The originator is responsible for managing its state and creating mementos. Ensure that the originator provides a clear and consistent API for saving and restoring its state.
- Limit the access to mementos within the originator and caretaker. External classes should not have direct access to mementos to prevent unintended modifications.
- The caretaker should not rely on the internal structure of the memento. It should interact with the memento solely through well-defined methods provided by the originator.
- If your application involves persisting or transmitting mementos, consider making the memento class serializable. Serialization allows mementos to be stored or sent across a network.
- Choose clear and meaningful names for methods in the memento class. Naming conventions such as getState and setState provide clarity regarding the purpose of the methods.
Bridge Design Pattern |
Facade Design Pattern |
Interpreter Design Pattern |
Factory Design Pattern |
Abstract Factory Design Pattern |
List of Design Patterns |