This is the ninth post in the “Design Patterns” series.
The previous one was about the visitor pattern and can be found here.
In this instalment, I want to talk about the memento pattern. You can use this pattern when you want to create a mechanism that can remember previous states. We use the undo functionality all the time, so grasping the concept shouldn’t be an issue for us.
The code presented in this post can be found here. The projects in the solution correspond with pattern names.
As usual, we are going to create a short console application. The application's goal is to showcase how the memento pattern allows us to record application states. The example I’m going to show is based on a hangman game. In this well-known game, a player tries to guess a hidden word with a limited number of attempts. You must have played it at least once.
But before we start analysing the code, let's dive into some theory.
As the old saying goes: The best place to start is at the start. So, we will begin with the definition of the memento pattern. It goes as follows: a memento pattern holds an object’s internal state so the object can be restored to this state later.
The memento pattern consists of three elements:
- Originator – this is the source object, so to speak. In this pattern, its inner state is to be remembered. It can be a game or a document we work on, and we use it when we want to undo or redo some operations.
- Memento – this is the part that memorises information, usually the inner Originator data. Based on these data collections, the state of the originator can be undone or redone.
- Caretaker – this is an element that works with memento pattern elements. It can be a piece of code, a user interface or a test tool.
That’s it when it comes to the theory. Now we will create a Hangman game and extend it with a memento pattern. Let’s start coding.
The one (correct) way
In the spirit of my previous article, we will use only one example. It is the natural way the memento pattern gets introduced to the code by extending the code that already exists.
In my case, the new code extending the originator class will be placed inside the derived class. It is not some kind of pattern standard or anything. By doing so, we can clearly separate the originator and the memento. Saying all that, let’s start with the originator class.
Massive. I know. But this is just a code that will help us to present a memento pattern. Plus, if you take a closer look, you will see there is nothing fancy about it. It is a very straightforward implementation of the Hangman game. It has only one method that can be called. The guess method allows us to make a guess and tells us the result of it by setting object properties. The rest of the presented code is just an infrastructure allowing the game object to work.
If you want to try the game at this point, you are free to do it using the OldWay method. It allows you to play the Hangman game on your console. Enjoy.
Now that we had our time with the code and maybe with the game it is time to introduce memento pattern. First, we are going to extend the class and after that use it in game mechanism implementation. In this example the memento mechanism got distributed into two files. The first one is the HangmanMemento class.
Its only purpose is to keep the data needed for the memento pattern to work. I know the class is super simple. Just keep in mind that the data used in the game is just a collection of charts.
The second part is in the HangmanGameWithUndo class.
The class inherits from an originator class and has two methods: to save and to load. Both are very typical for the memento pattern. The CreateSetPoint method allows us to use save game state by using the HangmanMemento class if we want to load data. To be honest, we don't load per se but roll back to some point, but you get the point. Now we use the ResumeFrom method. It sets the previous guess to the value that was provided. In our case, it's going to be one of the previous guessing states.
Now it's time to see how to use this undo in the game scenario. The full code is presented below.
In this code, we need to focus on two major areas. First is the way we initialize the game and its data state. In the beginning, we create the HangmanGameWithUndo object. After that, we create a stack on the memento object. In that case, the stack should be our go-to data structure because we only need to get the latest element. The first element in the stack is in the first data state, the empty one.
The second part is using new functionalities inside the code. The new capabilities are used in two places during the undo operation. Here we need to perform two operations. We need to pop the latest element from the stack and set the game state to be of the new stack top element.
The second part is when we make a guess. We need to create a set point in the scenario.
And that’s it. All the pieces are in place. We are free to play the Hangman game with an extra undo option. Enjoy.
The memento pattern allows us to introduce the undo and redo mechanism to our application. It helps us in this specific scenario by tracking data state history. What we do with that data is up to us.
As every single pattern, it should be used only when a value can be gained. Have you ever used it? Let me know at firstname.lastname@example.org.
Until next time. Keep coding!