Your Guide to Design Patterns – Memento Pattern

Design Patterns Memento Pattern

This week we’ll continue our exploration of design patterns and look at another behavioural pattern, the Memento pattern.

The Gang of Four book defines the Memento pattern as follows:

“Without violating encapsulation, capture and externalise an object’s internal state so that it can be restored to this state later.”

The Memento pattern provides an undo mechanism for an application when the internal state of an object possibly needs to be restored at a later stage. A simple example is that of a text editor where we can save text, and undo any changes to restore the text to its previous state. Rolling back a database transaction is another example.

The Problem

Occasionally we might need to save the internal state of an object in order to implement a restore point or an undo operation. We will have to save this state somewhere, so that we can restore the previous state of the object at a later stage.

An object usually encapsulates and hides its internal state. This state is then inaccessible to other objects, and cannot be saved by an external object. We could expose the state by making the internal fields public, but this violates encapsulation and compromises the object’s integrity.

We also want to conform to the Single Responsibility principle, so we don’t want the class to have its own save-and-restore mechanism. So how do we capture the state of an object, and possibly restore the object to a previous state?

The Solution: Memento Pattern

We solve this problem with the Memento pattern. The pattern is implemented with three objects: the originator, the memento and the caretaker:

  • The originator is some object that has an internal state.
  • The memento is an object that stores a snapshot of the internal state of the originator. The memento object is an opaque object, i.e., it can’t be read by other objects.
  • The caretaker is the client code that includes the undo functionality.

The sequence is as follows:

  • The caretaker wants to perform an operation using the originator, but wants to be able to undo any changes if necessary.
  • The caretaker asks the originator for a memento object when it needs to save the originator’s state.
  • The originator creates the memento with its current state.
  • The caretaker performs the operation on the originator.
  • If the caretaker needs to undo the operation and return to the previous state, it returns the memento object to the originator.
  • The originator uses the memento to restore its own state. Only the originator can store and retrieve information from the memento.

Ways to Implement the Memento Pattern

There are three fairly straightforward ways to implement the Memento pattern in Java:

  • Serialization. It’s very easy to save and restore object state using serialization. This is more generic than the original Memento pattern where every object has to have its own memento class. A disadvantage of serialization is that if the originator object is large, then the serialized memento object will also be large.
  • Packaging. We can declare a class in the same package that can access fields of the originator with default/package-private access. We have to tightly control which classes are contained in this package. Either the originator and the memento must be in a package by themselves, or other classes in the same package will have direct access to their instance variables.
  • Proxies. The object can be accessed via a proxy that can implement save/restore operations on the object.

Memento Pattern: Example Code

Our first person shooter game is coming along nicely. We can create various monsters and creatures, engage in battles and calculate damage. We can request reinforcements. We can upgrade vehicles with more weapons and armour.

We have now decided that players need to be able to save a game at any point and continue playing at a later stage. They do need to sleep and go to work occasionally…

Let’s create a class with one method to save the state of the game, and another to restore it.

For this we need to save the state of the game. We could decide to use a human readable file format like XML or JSON, but we don’t want to make it easy for anyone to cheat by editing the saved game files. A much better choice would be to use a binary format. A good solution for this would be to use the default Java binary serialization mechanism implemented in the ObjectOutputStream and ObjectInputStream classes.

import java.io.*;

public class GameMemento {

    // The player needs the option to change the filename 
    private String fileName = "game.sav";

    // Constructor
    public GameMemento(Game game) throws IOException {
        // call the save method
        save(game);
    }

    // Memento code
    public void save(Game game) throws IOException {
        // Serialize the current state of the game
        File gameFile = new File(fileName);
        ObjectOutputStream oos = new ObjectOutputStream(
                                     new BufferedOutputStream(
                                         new FileOutputStream(gameFile)));
        oos.writeObject(game);
        oos.close();
    }

    public Game restore() throws IOException, ClassNotFoundException {
        // Deserialize the previous state of the game...
        File gameFile = new File(fileName);
        ObjectInputStream ois = new ObjectInputStream(
                                    new BufferedInputStream(
                                        new FileInputStream(gameFile)));
        Game game = (Game) ois.readObject();
        ois.close();
        return game;
    }

} // end of class

The Game class will contain a collection of Combatant objects (players, monsters, vehicles, etc.).

import java.util.*;

public class Game implements java.io.Serializable {

    // collection of combatants
    private ArrayList combatants;

    public Game() {
        combatants = new ArrayList<>();
    }

    // methods to initialize the game, add and remove combatants, etc.
    //...

}  // end of class

Combatant object will contain data about its current position, movement, speed, health, weapons, armour, etc. – everything that we would need to restore the game to a previously saved state.

public class Combatant implements java.io.Serializable {

    // data fields for position, movement, speed, health, weapons, armour, etc.

    // constructors, accessors, canonical methods, business methods, etc.

} // end of class

Both the Game and the Combatant classes need to implement java.io.Serializable, so that they can be serialized by the memento class.

Example code for the client (caretaker) to save and restore state follows:

// Creating and initializing the game
 Game game = new Game();
 game.init();

 // Game running...

 // Save the state of the game when required
 System.out.println("Saving the state of the game...");
 GameMemento memento = new GameMemento(game);

 // State of the game is changed as players play...

 // Restore the game to the previous saved state
 System.out.println("Restoring game...");
 game = memento.restore();

 // Game runs from previously saved point...

Related Patterns

The Command pattern can use mementos to save state for any later undoable operations.

What’s Next?

In the weeks ahead, we’ll continue examining some of the more useful design patterns. Stay tuned!

As always, please share your comments and questions.

Leave a Comment

Your email address will not be published. Required fields are marked *

Code like a Java Guru!

Thank You

We're Excited!

Thank you for completing the form. We're excited that you have chosen to contact us about training. We will process the information as soon as we can, and we will do our best to contact you within 1 working day. (Please note that our offices are closed over weekends and public holidays.)

Don't Worry

Our privacy policy ensures your data is safe: Incus Data does not sell or otherwise distribute email addresses. We will not divulge your personal information to anyone unless specifically authorised by you.

If you need any further information, please contact us on tel: (27) 12-666-2020 or email info@incusdata.com

How can we help you?

Let us contact you about your training requirements. Just fill in a few details, and we’ll get right back to you.

Your Java tip is on its way!

Check that incusdata.com is an approved sender, so that your Java tips don’t land up in the spam folder.

Our privacy policy means your data is safe. You can unsubscribe from these tips at any time.