Design Pattern Examples
Overview of object-oriented design patterns
Memento Pattern

Diagram of the Memento pattern

The Memento design pattern is used to capture the internal state of an object without exposing the details of that internal state. At a later time, the Memento is used to restore the internal state of the object.

The client asks an object for a snapshot of the object's internal state. This snapshot is in an opaque form. The client typically caches this snapshot for later use (such as restoring the state to a known good state or as part of the process of implementing an undo system). When the client wants to restore the object's state, it passed the snapshot back to the object and the object changes its internal state to match the given snapshot.

The python pickling mechanism is a form of the Memento pattern, although in the process of pickling an object, the entire object is saved off, not just the internal state; however, the effect is ultimately the same.

To implement the Memento state, the object whose state is to be saved off and later restored must be designed to make it easy to save and restore the state. The Private Class Data Pattern can facilitate this by storing the state of the main class in another class. That data class can then be more easily converted to some internal state or the data class instance itself could be stored in the Memento, which is arguably more effective with classes that have a lot of internal data elements (less copying). A class with only a few data elements would be more efficient serializing the data elements into another form such as strings or binary images.

In the Command Pattern, a simplistic undo/redo stack was created that required remembering commands used to get to a particular state. To undo changes to the state, the state was cleared then the commands up to but not including the command to undo were replayed, rebuilding the state.

Using Mementos, the state can be saved before each command. To undo a command, the state saved before that command could be restored. No need to replay the commands at all. This makes for a more efficient undo process. However, if the state is very large, such as in a paint program, the undo may have to spool the saved states to disk or provide a combination of commands and mementos, where the state is saved periodically and the commands to get to the next save state are remembered and replayed.

How to Use

Links to the Memento class and interface
C++ C# Python C
IMemento interface IMemento interface IMemento interface Not Applicable
Memento class Memento class Memento class Memento structure
Memento_Create()
Memento_Destroy()
Memento_TextObject class Memento_TextObject class Memento_TextObject class Memento_TextObject structure
Memento_TextObject_Create()
Memento_TextObject_Destroy()
Memento_TextObject_GetText()
Memento_TextObject_SetText()
Memento_TextObject_ToString()

The example used here is a snapshot of a text object. This is then used to create an undo stack. In fact, this is the same example as used for the Command Pattern but this example shows how mementos are generally more efficient than commands for creating an undo stack.

C++

void Memento_Exercise()
{
std::cout << std::endl;
std::cout << "Memento Exercise" << std::endl;
// Start with a fresh undo list.
// The base text object to work from.
Memento_TextObject text("This is a line of text on which to experiment.");
std::cout
<< Helpers::formatstring(" Starting text: \"%s\"", text.ToString().c_str())
<< std::endl;
// Apply four operations to the text.
Memento_ApplyReplaceOperation(text, "text", "painting");
Memento_ApplyReplaceOperation(text, "on", "off");
std::cout << " Now perform undo until back to original" << std::endl;
// Now undo the four operations.
Memento_Undo(text);
Memento_Undo(text);
Memento_Undo(text);
Memento_Undo(text);
std::cout
<< Helpers::formatstring(" Final text : \"%s\"", text.ToString().c_str())
<< std::endl;
std::cout << " Done." << std::endl;
}
static void Memento_Undo(Memento_TextObject *text)
Perform an undo on the given Command_TextObject, using the mementos in the "global" undo list....
static void Memento_ApplyReverseOperation(Memento_TextObject *text)
Helper function to reverse the order of the characters in the given Memento_TextObject after adding a...
static void Memento_ApplyReplaceOperation(Memento_TextObject *text, const char *searchPattern, const char *replaceText)
Helper function to replace a pattern with another string in the given Memento_TextObject after adding...
static StackEntry * _mementoUndoList
The list of memento objects that form a series of snapshots in time of a Memento_TextObject.
std::string formatstring(const char *fmt,...)
Use the given string and arguments to return a buffer containing the formatted string....
Container for a string.

C#

public void Run()
{
Console.WriteLine();
Console.WriteLine("Memento Exercise");
// Start with a fresh undo list.
// The base text object to work from.
Memento_TextObject text = new Memento_TextObject("This is a line of text on which to experiment.");
Console.WriteLine(" Starting text: \"{0}\"", text);
// Apply four operations to the text.
Memento_ApplyReplaceOperation(text, "text", "painting");
Memento_ApplyReplaceOperation(text, "on", "off");
Console.WriteLine(" Now perform undo until back to original");
// Now undo the four operations.
Memento_Undo(text);
Memento_Undo(text);
Memento_Undo(text);
Memento_Undo(text);
Console.WriteLine(" Final text : \"{0}\"", text);
Console.WriteLine(" Done.");
}

Python

def Memento_Exercise():
print()
print("Memento Exercise")
# Start with a fresh undo list.
_mementoUndoList.clear()
# The base text object to work from.
text = Memento_TextObject("This is a line of text on which to experiment.")
print(" Starting text: \"{0}\"".format(text.ToString()))
# Apply four operations to the text.
Memento_ApplyReplaceOperation(text, "text", "painting")
Memento_ApplyReplaceOperation(text, "on", "off")
print(" Now perform undo until back to original")
# Now undo the four operations.
print(" Final text : \"{0}\"".format(text.ToString()))
print(" Done.")

C

void Memento_Exercise(void)
{
printf("\nMemento_Exercise\n");
// The base text object to work from.
Memento_TextObject* text = Memento_TextObject_Create("This is a line of text on which to experiment.");
if (text != NULL)
{
printf(" Starting text: \"%s\"\n", Memento_TextObject_ToString(text));
// Apply four operations to the text.
Memento_ApplyReplaceOperation(text, "text", "painting");
Memento_ApplyReplaceOperation(text, "on", "off");
printf(" Now perform undo until back to original\n");
// Now undo the four operations.
Memento_Undo(text);
Memento_Undo(text);
Memento_Undo(text);
Memento_Undo(text);
printf(" Final text : \"%s\"\n", Memento_TextObject_ToString(text));
}
printf(" Done.\n");
}
void Memento_TextObject_Destroy(Memento_TextObject *textObject)
Destroy the given Memento_TextObject object and release any used memory. After this function returns,...
Memento_TextObject * Memento_TextObject_Create(const char *text)
Create a new instance of the Memento_TextObject structure and initialize it with the given text.
const char * Memento_TextObject_ToString(Memento_TextObject *textObject)
Return a string representation of the Memento_TextObject. In this case, it is just the underlying tex...

RUST

(Apologies. Doxygen does not understand Rust syntax and therefore cannot colorize the code.)

pub fn memento_exercise() -> Result<(), String> {
println!("");
println!("Memento Exercise");
// Start with a fresh undo list.
let mut memento_context = MementoContext::new();
// The base text object to work from.
let mut text_object = MementoTextObject::new("This is a line of text on which to experiment.");
println!(" Starting text: \"{0}\"", text_object);
// Apply four operations to the text.
memento_context.apply_replace_operation(&mut text_object, "text", "painting");
memento_context.apply_replace_operation(&mut text_object, "on", "off");
memento_context.apply_reverse_operation(&mut text_object);
memento_context.apply_replace_operation(&mut text_object, "i", "!");
println!(" Now perform undo until back to original");
// Now undo the four operations.
memento_context.undo(&mut text_object);
memento_context.undo(&mut text_object);
memento_context.undo(&mut text_object);
memento_context.undo(&mut text_object);
println!(" Final text : \"{0}\"", text_object);
println!(" Done.");
Ok(())
}

See Also