Learn the Memento Design Pattern
This is the 23rd post in a series on design patterns.
Memento is a behavioral design pattern that lets you save and restore the previous state of an object without revealing the details of its implementation.
Consider that you are creating a text editor application. Apart from writing text, one of the basic requirements of a text editor is the ability to undo changes made.
Thus, you decided to use a direct implementation where, before performing any operation, the application records the state of all objects and stores it somewhere. As a result, the application retrieves the latest snapshot from the history when a user decides to revert an action.
The values of all the fields in the object would need to be copied into storage. However, this would be possible only if the object had relaxed access restrictions to its content, which is not the case in real life.
Memento Design Pattern
Broken encapsulation is the cause of the problem we just encountered. Objects sometimes try to do more than they are supposed to. To collect the data needed to perform a particular action, they invade the private space of the other objects instead of letting these objects perform the action.
The Memento pattern delegated the responsibility for creating snapshots of the state to the actual owner of the state, the originator object. Therefore, other objects should not try to copy the editor’s state.
The pattern suggests storing a copy of the object’s state in a special memento. The contents of the memento can only be accessed by the object that produced it. Mementos must communicate with other objects via a limited interface, which allows fetching the snapshot’s metadata but not the original object’s state.
Mementos can be stored in other objects, usually called caretakers, under such restrictive policies. The caretaker has limited access to the memento, so it cannot alter its state. As well, the originator has access to all fields within the memento, allowing it to restore its previous state at will.
When implementing undo, Command and Memento design patterns can be used together. Commands are responsible for performing various operations on a target object, while mementos record the state of that object just before a command is executed.
UML Class Diagram
Not familiar with UML Class Diagram? I have written a detailed post on the UML Class diagram.
Implementation steps
- Determine which class will be the originator. It’s crucial to know whether the program uses one central object or many smaller ones.
- Create a class called Memento. Declare one by one a set of fields corresponding to those inside the originator class.
- Make the memory class immutable. Mementos should only accept data once, via the constructor. The class should have no setters.
- Nest the memento inside the originator if your programming language supports nested classes. If not, extract a blank interface from the memento class and make all other objects use it to refer to the memento. You might add some metadata operations to the interface, but not anything that exposes the originator’s state.
- Add a method to the originator class for producing mementos. The originator should pass its state to the memento’s constructor via one or more arguments. The return type of the method should be that of the interface you extracted in the previous step. The memento-producing method should work directly with the memento class.
- Add a method for restoring the originator’s state to the class. The method should accept a memento object as an argument. If you extracted an interface in the previous step, make it a parameter. Because the originator needs access to the incoming object, typecasting is necessary.
- Regardless of whether a caretaker represents a command object, a history, or something entirely different, it should know when to request new mementos from the originator, how to store them, and when to restore the originator with a particular memento.
- We may move the link between caretakers and originators into the memento class. As a result, each memento must be associated with the creator who created it. Restoration will also move to the memento class. This would all make sense only if the memento class is nested inside the originator class or if the originator class provides sufficient setters for overriding its state.
Source Code Implementation
When needed, the Editor (Originator) class can create snapshots of its own state as well as restore its state from snapshots.
A Memento is a value object that acts as a snapshot of the originator’s current state. It’s common to make the memento immutable and to pass the data only once, via the constructor.
In addition to knowing when and why to capture the originator’s state, the Caretaker knows when to restore it. The caretaker can keep track of the originator’s history by storing mementos. When the originator has to go back in time, the caretaker retrieves the topmost memento from the stack and passes it to the originator’s restoration method.
The memento class is nested inside the originator class in this implementation. The originator can access the fields and methods of the memento, even though they have been declared private. Despite this, the caretaker has very limited access to the memento’s fields and methods, which allows it to store mementos on a stack without altering their state.
When To Apply Memento Design Pattern
- When you need to create snapshots of the object’s state in order to be able to restore it to a previous state, use the Memento pattern.
- Whenever direct access to the object’s fields violates its encapsulation, use this pattern. The Memento makes the object itself responsible for taking a snapshot of its state. The snapshot cannot be read by any other object, so the original object’s state data is safe and secure.
Pros of Memento Design Pattern
- Snapshots of the object’s state can be created without violating its encapsulation.
- Let the caretaker maintain the history of the originator’s state so that the originator’s code can be simplified.