Understanding The Memento Design Pattern

Nisal Pubudu
Nerd For Tech
Published in
6 min readMay 27, 2021

--

Photo by Jim Wilson on Unsplash

Memento Pattern (a.k.a. Snapshot Pattern) is a Behavioral design pattern, and it’s used to save and restore previous state of an object. If you want to develop an application, that has undo or rollback functionality, you should go with Memento Design Pattern. Most of the software developers use this pattern whenever they want to develop such functionality within their application.

A simple example to understand the Memento pattern’s concept is, Windows restore point system. In Windows, we create restore points because we can use those to restore our OS into a previous state, if something breaks or system crashes.

The Memento Design Pattern has 3 components know as, Originator, Caretaker, and Memento.

Originator: The object that we need to maintain the state. Basically, the Originator object creates a memento object to store its internal state. As a result, the Originator object knows how to save and restore itself.

Caretaker: The object that keeps the track about Originator. Basically, Caretaker knows why and when the Originator needs to save and restore itself.

Memento: The object that contains basic state storage and retrieval capabilities. Usually, the Memento object is immutable and pass it’s the data only once, through the constructor.

Image: Memento Pattern UML representation (dotnettricks.com)

When implementing Memento Design pattern, the Originator coupled with the Memento and pass the state into the Caretaker. So, whenever we need go back into the previous state, we have to talk to the Caretaker and we check the previous state, then move.

So, in the next chapter, I will explain how to apply Memento Design Pattern with a real-world example using Java.

Use case of Memento Design Pattern

Assume there is a sport club, that require a software to keep track on their players’ rank. The player rank will be measured according to two facts: “performance” and “health state.” So, the sport club considers these two facts, when assigning ranks to their players in every training session.

In a training session, a player can score performance marks up to 100 and lowest is 0. The health state comes with 2 different states named as, “Healthy” and “Unhealthy.” If a player can perform well and get marks more than 80 while getting “Healthy” as the health state, that player will receive “Excellent” rank in that session. Also, there are 4 more ranks, known as Good, Average, Poor, and Very Poor. But all these marks, health states, and ranks valid only for that particular training session. Whenever they begin a new session in another day, players will be assigned with new marks and states for that session.

Additionally, the sport club required a functionality to undo the progresses of sessions. Because there are some situations, the coach might need to undo the recent sessions details and go back to a previous session. Especially when a player tests positive for a drug test or if they perform any unlawful act, their participation in several sessions will be removed. As a result, the coach has to roll back their progress according to that particular scenario.

Now let’s see the implementation of this example.

Step 01:

As the first step I have created a class called “Rank”. Within this class I have defined 3 fields named as “performance”, “health”, and “rank.” Also, I have declared a method named as “rankGenerator()” that responsible for generating the player rank based on performance and health state. Refer the following gist.

Also, I have created an Enum to hold the health state types.

Step 02:

As the second step, I have created the “Player” class as the Originator and “RankMemento” class as the Memento. Within the “Player” class I have implemented a method called “addRank()” to add the rank details of players to an Array List. Then I have implemented a method called “getRankList()” to return a cloned object from the Array List. The reason for using “clone()” method is, to prevent the referring to original object.

In this application, the Memento class (RankMemento) is implemented within the Originator class (Player). But if you want, you can implement those two classes separately. Moreover, within the same Originator class I have created two specific methods named as “saveRank()” and “revertRank()”.

The “saveRank()” method is responsible for giving the state of the player to the Caretaker (RankHistory). Therefore, it return an instance of “RankMemento” by passing a cloned object from the Array List.

The “revertRank()” method is responsible for reverting the current state.

Step 03:

Now I have implemented the “RankHistory” class as the Caretaker. This class is responsible for keep track on “RankMemento” class. Within the “RankHistory” class, I have created a Stack to store objects of “RankMemento” class as Last In, First Out (LIFO) order. So, when we use “reverRank()” method, we can have the last object save within the Stack. Refer the following gist.

Step 04:

At last we can run our application to check the output. Refer the following gist.

Output:

Player{rankList=[Rank{, rank='Excellent'}, Rank{, rank='Good'}]}Player{rankList=[Rank{, rank='Excellent'}, Rank{, rank='Good'}, Rank{, rank='Very Poor'}]}Player{rankList=[Rank{, rank='Excellent'}, Rank{, rank='Good'}, Rank{, rank='Very Poor'}, Rank{, rank='Poor'}]}

As you can see on the gist, the coach has added 2 training session details (Line 9 & 10) and save those using the “saveRank()” method (Line 13). Assume there were 2 training session on that week. Within the next week there was another session and he has to add those details too (Line 16). Again he uses “saveRank()” method and repeat the same thing for another week.

As you can see on the output, player ranked generated according to the passed arguments and system is working as expected so far. Now lets see how we can revert those using “revertRank()” method.

Player{rankList=[Rank{, rank='Excellent'}, Rank{, rank='Good'}]}Player{rankList=[Rank{, rank='Excellent'}, Rank{, rank='Good'}, Rank{, rank='Very Poor'}]}Player{rankList=[Rank{, rank='Excellent'}, Rank{, rank='Good'}, Rank{, rank='Very Poor'}, Rank{, rank='Poor'}]}------------------Start Reverting------------------Player{rankList=[Rank{, rank='Excellent'}, Rank{, rank='Good'}, Rank{, rank='Very Poor'}, Rank{, rank='Poor'}]}Player{rankList=[Rank{, rank='Excellent'}, Rank{, rank='Good'}, Rank{, rank='Very Poor'}]}Player{rankList=[Rank{, rank='Excellent'}, Rank{, rank='Good'}]}Nothing left to UndoPlayer{rankList=[Rank{, rank='Excellent'}, Rank{, rank='Good'}]}

As you can see on the output, even we called the “revertRank()” method, it looks like it doesn’t affect at first. However, other method-calls worked as expected. So, what’s wrong with the first method call?

What happen here is, we add a new rank to the player and gave it to the Caretaker. After that, we called the “revertRanked()” method on Line 28. That means we are asking from Caretaker to give us the previous rank details of a player. So, what Caretaker does is, it will give us the last saved rank details, since the “revertRanked()” method return it from the Stack. So, this returned object (rank details) is none other than the last object we saved before calling our revert method. That’s the reason, why we see this result in the above output. Therefore, we need to remove the “saveRank()” method-call on line 23, and it will work as expected.

Moreover, if you confused about why the output prints “Nothing left to Undo” even though it has 2 more items (rankings). It simply because it has nothing to do with a single item, but instead with the version/set. If you pass those to the Caretaker one by one, those will be reverted one by one. But if you save several items (rankings) at a time, those will be reverted as a single version/set.

If you are interested, you can use the following GitHub link to see my complete implementation of Memento Design Pattern.

So, this is the end of my article and I hope you enjoyed it. Happy Coding👨‍💻.

--

--