Video games, electronic documents, and computers in general would not be very useful if we couldn't retrieve previous state; we would never be able to save our progress! In object oriented programming, a design pattern that is useful for maintaining previous application state during runtime is called the Memento Pattern.

Assumptions

What is the Memento Pattern?

A Memento is a class that stores the previous state of an object in order to restore from that state at a later time.

<div style="width:100%; margin:auto;text-align:center;"><img src="https://www.devmaking.com/img/topics/designpatterns/MementoPattern_01.png" alt="memento pattern UML diagram" style="max-width:95%;"> </div>

Key components:

  • Memento: class that stores a state object.
  • Caretaker: maintains a list of previous mementos.
  • Originator: holds onto a Memento object and can have it's state restored.

Like the <a href="https://www.devmaking.com/learn/design-patterns/command-pattern/" target="_blank" style="color:inherit;">Command Pattern</a>, the memento pattern is useful for conducting undo operations as it stores a list of previous states which can be restored at any time.

Conceptualization

To demonstrate the memento pattern in action, we'll be creating a simple game called dragon attack. In this game, a mighty warrior and fearsome dragon face off against each other in a fiery battle.

Let's start off by defining our Memento class and the GameState that it will contain. Our game state will be simple since it only needs to store two integers:

class Memento {
   GameState state;
   Memento (GameState state) {
     this.state = state;
   }
   
   GameState getState() {
     return state;
   }
 }

class GameState {
   dragonHealth;
   warriorHealth;
   
   GameState(int dragonHealth, int warriorHealth) {
     this.dragonHealth = dragonHealth;
     this.warriorHealth = warriorHealth;
   }
 }

Our memento object will act as a "snapshot" of the game state at a particular point in time. Next, we need to define our Originator that will maintain the current copy of the game state. Additionally, it should be able to save a new memento object with a snapshot of the state:

class Originator {
   GameState state;
   
   void setState(GameState state) {
     this.state = state;
   }
   
   GameState getState() {
     return state;
   }
   
   Memento save() {
     // Depending on your implementation, you may have to copy() state!
     return new Memento(state);
   }
   
   void getStateFrom(Memento memento) {
     this.state = memento.getState();
   }
 }

Next, we want to define our Caretaker class that maintains a list of Memento objects for each prior game state. This is fairly straightforward:

class Caretaker {
   List&lt;Memento&gt; gameHistory = new List&lt;&gt;();
   
   void addMemento(Memento memento) {
     gameHistory.add(memento);
   }
   
   Memento undo() {
     return gameHistory.pop();
   }
 }

As you can see, we have an additional method, undo that simply returns the most recent memento saved to it.

Finally, we have our classes defined and are ready to create our game!

Client Code:

class DragonAttack {
   static void main(String[] args) {
     
     print("The Warrior approaches!");
     
     // Create our originator and caretaker:
     Originator originator = new Originator();
     Caretaker caretaker = new Caretaker();
     // Set the initial state of our originator:
     originator.setState(new GameState(100, 100));
     // Save the state:
     caretaker.addMemento(originator.save());
     
     // The warrior and dragon will take 3 turns each:
     for(int i = 0; i &lt; 3; i++) {
       print("Turn " + (i + 1));
       // Calculate damage:
       int warriorDamage = Random.randInt(0,20);
       int dragonDamage = Random.randInt(0,20);
       // Calculate new health left:
       int newWarriorHealth = originator.getState().warriorHealth - warriorDamage;
       int newDragonHealth = originator.getState().dragonHealth - dragonDamage;
       // Save the new game state:
       originator.setState(new GameState(newWarriorHealth, newDragonHealth));
       caretaker.addMemento(originator.save());
      
       print("WarriorHealth: " + newWarriorHealth);
       print("DragonHealth: " + newDragonHealth);
       print("-----");
     }
     // Undo the final turn:
     print("Undoing the last attack:");
     originator.getStateFrom(caretaker.undo());
     
     print("WarriorHealth: " + originator.getState().warriorHealth);
     print("DragonHealth: " + originator.getState().dragonHealth);
     
   }
 }

> Challenge: implement this in your favorite language with user input!

Output:

The warrior approaches!
Turn 1
WarriorHealth: 94
DragonHealth: 89
-----
Turn 2
WarriorHealth: 80
DragonHealth: 74
-----
Turn 3
WarriorHealth: 68
DragonHealth: 60
-----
Undoing the last attack:
WarriorHealth: 80
DragonHealth: 74