Sometimes in an application, you need to resort to your previous selection, undo your current move or want to restore the original state. This is impossible unless you have a snapshot of the previous state.
However, the internal state of an object remains inaccessible most of the time to preserve the encapsulation and reliability of the application. The solution to this issue is the Memento Design Pattern.
The Memento design pattern creates a snapshot of the internal state of an object at any instant of time and makes it available for access externally. A Memento is an object that captures another object’s state, the Originator.
This article teaches how Memento Design Pattern helps you rectify errors or restore previous choices, its implementation, and real-world applications.
What is the Memento Design Pattern?
The Gang of Four (GOF) introduced the design patterns in their book Design Patterns: Elements of Reusable Object-Oriented Software as a standard and optimal solution to frequently occurring programming scenarios.
One of these patterns is the Memento Design Pattern, which is a behavioral design pattern, i.e., the design patterns that deal with the communication and interconnections of objects with each other and other classes.
The Memento Design Pattern deals with how objects access each other to revert to the previous states.
In this case, the Memento is entirely passive and sees two interfaces: the Originator and the Caretaker. The Originator is the one that can write its state to the Memento and pass it to the Caretaker when needed. However, the Caretaker cannot access the Memento.
A common Caretaker would be the Undo operation in a text editor. Whenever a user performs this operation, the undo mechanism requests the previously saved copy from the Originator. The Originator then loads the state from the Memento and passes it to the Caretaker, which restores the previous copy of the file.
The Memento pattern comes in handy when an application needs to revert to previous checkpoints, whether in a text editor like Word, a game, or an online form.
When to use the Memento Design Pattern?
Now that you understand the Memento Pattern, it’s time to see when you need this pattern to make your code more straightforward and practical.
When you need a state later
If your application requires checkpoints to which you might need to revert back to, for instance, respawning in a shooting game from the last position, you should implement the Memento pattern.
In this case, the game would ask an object for its latest saved stage; let’s call this object the GameStage. Now the GameStage creates a Memento and passes it to the Game itself, which safeguards it. When the user dies at any point, the Game passes the last saved stage to the GameStage. In this way, the user can resume playing from their last position.
However, if the user’s lives run out, the Game passes the initial state to the GameStage, causing the game to reset from the beginning. The initial state will also be present in the Memento List.
In this way, you can use the states at later time instants by keeping their copies saved.
When direct communication would break encapsulation
Suppose you know that a direct interface to an object would expose its implementation details, ultimately providing external access to the object’s crucial data. In that case, you should use the Memento pattern.
If you give unauthorized access to your object, it might completely change and behave differently than intended at any point in time. To prevent such errors, you can use a state snapshot as a Memento, and pass it to the main operation instead of directly accessing the state of an object.
When you need to keep track of increments
Sometimes, you need not just one state but a set of previous states.
When the Memento keeps track of the states in a specific sequence, it can keep the snapshots of increments to reduce the processing power.
For instance, if you have two objects on the screen that you need to keep connected with a line, it’s only possible by solving a constraint equation every time they are moved. The Memento, in this case, can be the distance moved by the objects instead of their absolute positions.
How to implement the Memento pattern in C# – Structural code
To thoroughly understand the Memento pattern, you need to understand its structural components of it.
Fortunately, this pattern is extremely simple, having only three participants: the Memento, the Originator, and the Caretaker:
- Originator (GameStage) – The object whose state needs to have a copy. It has the ability to create a Memento and retrieve information from it. In other words, it sees a wide interface to the Memento and has the ability to view its implementation details.
- Memento (The last played stage) – Memento is the copy that the Originator generates after specific intervals so it can revert back to it.
- Caretaker (Game) – The mechanism that asks for the Memento, safeguards it, and passes it back to the Originator to restore its original state. It cannot access the internal state of the object. In other words, it sees a narrow interface to the Memento, having only the ability to pass the Memento.
The Caretaker asks the Originator to create a Memento. The Originator makes a copy of its original state inside a Memento object and passes this object to the Caretaker. The Caretaker keeps hold of the Memento object (or list) unless it needs to revert back. In such a case, it passes on the Memento back to the Originator, overwriting its current state and restoring some previous state.
Let’s implement this UML structure using C#.
public class Originator
{
int _statesnap;
public int State
{
get { return _statesnap; }
set
{
_statesnap = value;
Console.WriteLine("State is " + _statesnap);
}
}
public Memento CreateMemento()
{
return new Memento(_statesnap);
}
public void RestoreMemento(Memento memento)
{
Console.WriteLine("The Previous State was: ");
State = memento.Statesnap;
}
}
public class Memento
{
int _statesnap;
public Memento(int statesnap)
{
this._statesnap = statesnap;
}
public int Statesnap
{
get { return _statesnap; }
}
}
public class Caretaker
{
Memento memento;
public Memento Memento
{
set { memento = value; }
get { return memento; }
}
}
And the usage:
Originator origin = new Originator();
Random rand = new Random();
origin.State = rand.Next(1000000);
// Creating a Memento
Caretaker caret = new Caretaker();
caret.Memento = origin.CreateMemento();
//Changing the state
origin.State = rand.Next(1000000);
// Restoring the State
origin.RestoreMemento(caret.Memento);
// Wait for user
Console.ReadKey();
The output of the code is as follows:
State is 362004
State is 450073
The Previous State was:
State is 362004
Memento pattern – Real-world example in C#
A simple usage of the Memento Design Pattern can be the Undo or previous function in a calculator.
For example, while doing calculations, you might add a wrong value or use incorrect digits. Instead of starting to add or subtract all the readings again, you can jump to the previous states and start from there.
The following example is based on this concept. But to keep it simple, we have stuck to an adder and have added a single checkpoint instead of saving a list of previous states. However, you can easily expand this code to create a full-fledged calculator with an undo function.
Here the Originator class is the adder whose copy is required, and the Caretaker class is the undo function, which calls the Memento and gives it back to the Originator to revert the addition.
class Adder
{
private int _result;
public Adder(int i = 0)
{
_result = 0;
}
public void SetResult(int i = 0)
{
this._result = 0;
}
public void Add(int x)
{
_result += x;
}
public int Solution()
{
return _result;
}
public Memento CreateMemento()
{
return new Memento(_result);
}
public void SetMemento(Memento memento)
{
_result = memento.State;
}
}
public class Memento
{
int state;
public Memento(int state)
{
this.state = state;
}
public int State
{
get { return state; }
}
}
public class Undo
{
Memento memento;
public Memento Memento
{
set { memento = value; }
get { return memento; }
}
}
The usage of the above code is as follows:
Adder adder = new Adder();
adder.Add(5);
adder.Add(10);
adder.Add(20);
Undo undo = new Undo();
undo.Memento = adder.CreateMemento();
adder.Add(100);
Console.WriteLine($"The solution of addition is:{adder.Solution()}");
adder.SetMemento(undo.Memento);
Console.WriteLine($"The solution at first checkpoint is:{adder.Solution()}");
Console.Read();
The output is:
The solution of addition is: 135
The solution at the first checkpoint is: 35
Benefits and Drawbacks of using the Memento pattern
While the Memento Pattern is meant to resolve the issue of state access, it does have its downsides. To use this pattern wisely, you need to know what benefits it brings and what disadvantages you might have to deal with by using this design pattern.
Benefits:
- No Compromise on Encapsulation – Memento pattern ensures that encapsulation is conserved whenever you store an object’s state. The information that an Originator manages is kept hidden from others, and Memento protects your programming objects from the complexity of the internal working of the Originator. Encapsulation is an important part of Object-Oriented Programming, and Memento ensures that.
- Object Recovery – With the Memento pattern, you can restore your object to its previous state whenever there is any failure or error in the program. This will help you with retaining information and try things without worrying about changing your object’s state.
- Keep Object History – Memento patterns enable you to keep the history of your object and its states.
- Less Burden on Originator – Instead of making the Originator keep the states of the objects, the clients will take care of the state of the object with the Memento pattern. This reduces the stress of managing and storing states in the Originator.
Drawbacks:
- Language Issues – Some programming languages do not make it easy to keep the Memento’s state hidden and make it accessible to only the Originator.
- High Costs – Memento pattern will consume more RAM space and hence, will be more expensive, especially if the users create multiple Mementos or if the Originator store too much information in them.
- Performance Drop – When saving the states of the objects for later use, it will somewhat reduce the performance of the Memento pattern due to the time and resources needed.
- Obsolete Mementos – To be able to destroy obsolete Mementos, Caretakers need to keep track of the Originator’s state lifecycle.
What are the related patterns to the Memento design pattern?
Memento Design Pattern has close relations with a couple of GoF patterns too. These are:
- Iterator Pattern – You can use Mementos in combination with Iterators to save each iteration state and roll back to it when required. If these changes occur in a sequence, the Memento can be the incremental change instead of the absolute values as discussed earlier.
- Command Pattern – For an undo operation, Mementos can handle the state information. So, before a command gets executed, the Memento saves a copy of the state for future use.
Conclusion
Memento Design Pattern is a behavioral design pattern that stores the object’s internal state without exposing its implementation details. It does this by providing a narrow interface to a Caretaker object and a wide interface to the Originator object whose copy is required.
While this pattern resolves the encapsulation problem and keeps the history of operations, it has added an extra processing load and may result in a performance drop.
Therefore, you should use this pattern only if the Mementos to be made are finite or if you can flush out the obsolete states from the list.
If you want to learn more about design patterns, check the separate in-depth article about design patterns in C#.