Memento is a behavioral design pattern that lets you capture the object's internal state without exposing its internal structure, so that the object can be returned to this state later.
Imagine that you are writing a text editor. The core logic resides in the main
Editor class. Other features, such as different text formattings, inline images, etc. live in separate command-centric classes.
At some point, you decide to make all user actions reversible. Or in other words, add the "undo" feature. To implement it, you will need to save the state of the
Editor before executing any operation. Later, if the user decides to revert some of his actions, the program will take a snapshot from the history and restore the
Editor's past state.
In order to copy the state of an object, you have to go over its fields and copy their values. But an object must have quite relaxed access to its contents, to let other objects peek inside and copy its contents, including sensible data.
While this approach would simply do the job and allow every class to produce the backup of editor's state at will, it is far from the ideal. At some point, you might decide to refactor the
Editor class and add or remove some of its fields. This would require changing all other classes that have been messing around with
But there is more. Let's consider the actual "backups" of the
Editor's state. Even the most primitive editor must have several fields for storing its data, such as the actual text, cursor coordinates, current scroll position, etc. In order to make a backup, you have to record all these values and put them into some container.
The containers would probably end-up being objects of some class. The class would have lots of fields that mirror editor's state and almost no methods. To allow other objects to write data to the backup object and read from it, you would probably need to make its fields public. But that will cause the same problem as the unrestricted
Editor class. Other classes will be affected by any change to the
It looks like a dead end. You either expose the internals of a class, making all related classes too fragile, or restrict access to its state, making it impossible to implement the "undo." Is there any other way?
The source of the problems described above is broken encapsulation. It often happens when some objects try to do more than they suppose to. Instead of asking other objects to do something for them, these objects invade private space of the other objects to collect the data, required to perform an action.
The Memento pattern delegates creating state snapshots to the actual owner of that state, the Originator object. Hence, instead of other objects trying to copy the
Editor's state from the "outside," the
Editor itself can make the snapshot since it has full access to its own state.
The pattern provides a special object for storing a snapshot of the object's state, called Memento. The memento's content is not accessible to any other object except the one that produced it. Other objects would communicate with mementos using a limited interface, which allows no more than fetching a snapshot meta data, such as a creation time, label, etc.
Such data protection allows storing mementos within other objects, called Caretakers. Since they have only limited interface to access memento, caretakers are not able to tamper with the state stored inside.
In our text editor example, we can create a separate
History class that will act as a caretaker. It will store a stack of mementos, growing each time an operation is about to be executed. At some point, when a user triggers the undo, the
History will take the most recent memento from the stack and pass it back to the
Editor, requesting a roll-back. Since the
Editor has full access to the memento, it will override its own state with the values taken from the memento.
Implementation with nested classes
The classic implementation relies on the nested classes support, available in many popular programming languages (such as C++, C# and Java).
Implementation with the indermediate interface
An alternative for programming languages that do not support nested classes (such as PHP).
Implementation with even stricter encapsulation
Useful when you do not want to leave even the remote chance of other classes accessing the state of Originator via Memento.
Originator contains the complex state that you do not want to expose to other objects. It can produce snapshots of its own state, as well as restore the state from snapshots when requested.
Memento is a value object that acts as a snapshot of the Originator's state. It is a common practice to make Memento immutable and pass it data only once, via constructor.
Caretaker knows not only when and why to capture originator's state, but also when the state should be restored.
A caretaker can keep a history of originator's state by storing a stack of mementos. When originator has to go back in history, the caretaker fetches the last memento from a stack and passes it to the originator's restoration method.
In this implementation, the Memento is nested inside the Originator, which has full access to its fields and methods, even though they are declared private. On the other hand, the Caretaker has very limited access to the Memento's fields and methods, which is just fine for storing mementos but is not very good for tampering with their state.
In the absence of nested classes, caretaker's access to memento can be restricted by forcing it to work with memento through a limited intermediary interface.
On the other hand, the originator would work directly with the memento class and have access to its public methods, not available to the caretaker.
This implementation allows having multiple types of originators and mementos. Each originator would work with corresponding memento class. Neither originators nor mementos expose their state to anyone.
Caretaker can not even indirecty change the state stored in memento now. Moreover, it becomes independent from the originator. The link to the originator moves to the memento class, along with the restoration method.
Each memento becomes linked to the specific originator. The originator passes itself to the memento's constructor, along with the state values. Thanks to the close relation between concrete mementos and originators, memento can restore the state of its originator, given that later has appropriate setters.
In this example, Memento is used alongside the Command pattern to store snapshots of a complex text editor object and restore it when needed.
A command object acts as caretakers and requests a memento from the editor before executing its actions. When a user attempts to roll-back an operation, the memento from the previous command can revert the editor's state.
The memento does not have any public fields, getters or setters. Therefore no object can alter its contents. Mementos are tied to the editor that created it and capable of restoring their state at will. This allows the application to support several independent editor windows.
When you need to make snapshots of some objects in order to restore their state later.
The Memento pattern allows producing full copies of an object's state and storing them separately from the object. While the "undo" application of the pattern has become quite common, it also indispensable when dealing with transactions (if you need to roll-back an operation on error).
When direct access to the object fields/getters/setters violates its encapsulation.
The Memento makes the object itself responsible for creating a snapshot of its state. No other object is able to read the snapshot, making the object's state safe and secure.
How to Implement
Determine what class will play a role of the originator. It is important to know whether the program uses one central object of this type or multiple.
Create the memento class. One by one, declare fields that mirror fields inside the originator class.
Make memento objects immutable. They should get initial values just once, via the constructor. There should be no setters in the memento class.
If your programming language supports nesting, make the memento an inner class inside the originator.
If not, extract a blank interface from the memento class and make all other objects refer to memento using it. You may add some meta data operations to the interface, but nothing that exposes originator's state.
Add a method for producing mementos to the originator class. Originators should create new instances of the memento by passing its field values to the memento's constructor.
The return type of the method should be of the interface you extracted in the previous step (in case if you extracted it at all). But inside, it should work directly with the memento class.
Add a method for restoring the state to the originator class. It should accept a memento object as an argument. Follow the same logic for assigning the type of the parameter as in the previous step.
The caretaker, whether it is a operations history, command objects, or something entirely different, should know when to request new mementos from the originator, how to store it and when to restore.
The link between caretakers and originators may be moved to the memento class. In such case, each memento will correspond to a specific originator object. They will also be responsible for restoring their originators' state. But this is only possible either if the memento class is nested into originator's or if the originator class provides sufficient setters for overriding its state.
Pros and Cons
- Does not violate the originator's encapsulation.
- Simplifies the originator's code by allowing a caretaker to maintain the history of originator's state.
- May require lots of RAM if clients create mementos too often.
- Caretakers should track the originator's lifecycle in order to destroy obsolete mementos.
Relations with Other Patterns
Command and Memento can be used together. They can act as magic tokens to be passed around and invoked at a later time. In Command, the token represents a request; in Memento, it represents the internal state of an object at a particular time. Polymorphism is important to Command, but not to Memento because its interface is so narrow that a memento can only be passed as a value.
Prototype can be the simpler alternative to the Memento, if the object, which state you want to store in history, is fairly straightforward, doesn't have links to external resources, or if the links are easy to re-establish.