What is the Memento Design Pattern?

Göksu Deniz
8 min readDec 24, 2022

--

Image is created by DALL-E 2 AI

The Memento design pattern is a behavioral design pattern that allows an object to capture the internal state of itself so that it can be restored later. This is useful when you want to be able to undo or redo changes to an object, or when you want to be able to save and restore the state of an object at a later time.

The Memento pattern typically involves three main components:

  1. Originator: This is the object that has an internal state that needs to be saved and restored. It exposes a method to create a memento object that captures the current internal state of the object.
  2. Memento: This is an object that captures the internal state of the originator object. It is typically implemented as a simple data structure that stores the relevant state information.
  3. Caretaker: This is the object that is responsible for saving and restoring the state of the originator object. It maintains a list of memento objects and can revert the originator object to a previous state by restoring a memento from the list.

Imagine you are using a word processor to write a document. The word processor has an “undo” and “redo” feature that allows you to undo or redo changes you have made to the document. The Memento design pattern is used to implement this feature.

Here’s how it works:

  1. First, you have an object called the “Originator” that represents the document you are editing.
  2. Next, you have an object called the “Memento” that stores a snapshot of the document at a particular point in time.
  3. Finally, you have an object called the “Caretaker” that keeps track of all the Mementos and can help you undo or redo changes to the document by using a Memento.

Whenever you make a change to the document, the word processor creates a Memento and gives it to the Caretaker. The Caretaker adds the Memento to a list of Mementos that it maintains.

When you click the “undo” button, the word processor asks the Caretaker for the Memento that was created just before the change you want to undo. It then asks the Originator to use that Memento to restore the document to the state it was in just before the change.

When you click the “redo” button, the word processor asks the Caretaker for the Memento that was created just after the change you want to redo. It then asks the Originator to use that Memento to restore the document to the state it was in just after the change.

By using the Memento design pattern, the word processor is able to implement an undo/redo feature that allows you to easily undo or redo changes to your document.

Here is an example of how the Memento pattern might be used to implement an undo/redo feature:

// The Originator class represents the object that has an internal state that needs to be saved and restored.
class Originator
{
private string state;

public string State
{
get { return state; }
set
{
state = value;
Console.WriteLine("State = " + state);
}
}

// Creates a memento object that captures the current internal state of the originator.
public Memento CreateMemento()
{
return new Memento(state);
}

// Restores the internal state of the originator from a memento object.
public void SetMemento(Memento memento)
{
Console.WriteLine("Restoring state...");
State = memento.State;
}
}

// The Memento class represents a snapshot of the internal state of the originator. It is a simple data structure
// that stores the relevant state information.
class Memento
{
public string State { get; private set; }

public Memento(string state)
{
State = state;
}
}

// The Caretaker class is responsible for saving and restoring the state of the originator. It maintains a list of
// memento objects and can revert the originator object to a previous state by restoring a memento from the list.
class Caretaker
{
private List<Memento> mementos = new List<Memento>();

public void AddMemento(Memento memento)
{
mementos.Add(memento);
}

public Memento GetMemento(int index)
{
return mementos[index];
}
}

class Program
{
static void Main(string[] args)
{
// Create the originator object and set its state.
Originator originator = new Originator();
originator.State = "State1";

// Create a caretaker object and add a memento object to its list.
Caretaker caretaker = new Caretaker();
caretaker.AddMemento(originator.CreateMemento());

// Change the state of the originator and add another memento object to the caretaker's list.
originator.State = "State2";
caretaker.AddMemento(originator.CreateMemento());

// Change the state of the originator and add another memento object to the caretaker's list.
originator.State = "State3";
caretaker.AddMemento(originator.CreateMemento());

// Print the current state of the originator.
Console.WriteLine("Current state: " + originator.State);

// Restore the originator to the state it was in when the second memento object was created.
originator.SetMemento(caretaker.GetMemento(1));
Console.WriteLine("Current state: " + originator.State);

// Restore the originator to the state it was in when the first memento object was created.
originator.SetMemento(caretaker.GetMemento(0));
Console.WriteLine("Current state: " + originator.State);
}
}

In this example, the Program class creates an Originator object and sets its state to "State1". It then creates a Caretaker object and adds a memento object to its list using the AddMemento method. The state of the Originator object is then changed to "State2" and another memento object is added to the caretaker's list. This process is repeated for the state "State3".

The Program class then prints the current state of the Originator object and restores it to the state it was in when the second memento object was created using the SetMemento method and the GetMemento method of the Caretaker object. The current state of the Originator object is then printed again. Finally, the Originator object is restored to the state it was in when the first memento object was created, and the current state is printed once more.

Here is an example of how the Memento design pattern might be implemented in C# to implement an undo/redo feature for a simple text editor step by step:

1. Create Memento class to capture the relevant state information.

// The Memento class represents a snapshot of the internal state of the originator. It is a simple data structure
// that stores the relevant state information. In this case, the Memento has a single string field called "text" that
// stores the contents of the text editor at a particular point in time.
class Memento
{
public string Text { get; set; }

public Memento(string text)
{
Text = text;
}
}

2. Create Originator class to represents the current state.

// The Originator class represents the object that has an internal state that needs to be saved and restored.
// In this case, the Originator is a simple text editor with a single string field called "text" that represents the
// current contents of the editor.
class Originator
{
public string Text { get; set; }

// Creates a memento object that captures the current internal state of the originator.
public Memento CreateMemento()
{
return new Memento(Text);
}

// Restores the internal state of the originator from a memento object.
public void SetMemento(Memento memento)
{
Text = memento.Text;
}
}

3. Create Caretaker class to save and restore states of memento objects.

// The Caretaker class is responsible for saving and restoring the state of the originator. It maintains two stacks of
// memento objects: an "undo stack" for undoing changes, and a "redo stack" for redoing changes.
class Caretaker
{
private Stack<Memento> undoStack = new Stack<Memento>();
private Stack<Memento> redoStack = new Stack<Memento>();

// Saves a memento object on the undo stack.
public void SaveMemento(Memento memento)
{
undoStack.Push(memento);
redoStack.Clear();
}

// Restores the state of the originator from the top memento object on the undo stack and pushes it onto the
// redo stack.
public void Undo()
{
if (undoStack.Count > 0)
{
Memento memento = undoStack.Pop();
redoStack.Push(memento);
memento.Originator.SetMemento(memento);
}
else
{
Console.WriteLine("Nothing to undo!");
}
}

// Restores the state of the originator from the top memento object on the redo stack and pushes it onto the
// undo stack.
public void Redo()
{
if (redoStack.Count > 0)
{
Memento memento = redoStack.Pop();
undoStack.Push(memento);
memento.Originator.SetMemento(memento);
}
else
{
Console.WriteLine("Nothing to redo!");
}
}
}

4. Create TextEditor class which has Undo/Redo functionality.

// The TextEditor class represents a simple text editor that uses the Memento design pattern to implement an undo/redo
// feature.
class TextEditor
{
private Originator originator = new Originator();
private Caretaker caretaker = new Caretaker();

// Makes a change to the text in the editor and saves a memento of the current state on the undo stack.
public void EditText(string text)
{
originator.Text = text;
caretaker.SaveMemento(originator.CreateMemento());
}

// Undoes the last change made to the text in the editor.
public void Undo()
{
caretaker.Undo();
}

// Redoes the last change made to the text in the editor.
public void Redo()
{
caretaker.Redo();
}

// Returns the current contents of the text editor.
public string GetText()
{
return originator.Text;
}
}

5. Use it on client:

class Program
{
static void Main(string[] args)
{
TextEditor editor = new TextEditor();

// Make some changes to the text in the editor.
editor.EditText("Hello, world!");
editor.EditText("Goodbye, world!");

// Undo the last change.
editor.Undo();

// Redo the last change.
editor.Redo();

// Print the current contents of the text editor.
Console.WriteLine(editor.GetText());

Console.ReadKey();
}
}

6. Take a cup of tea before you try undo.

That’s all.

Conclusion

The Memento design pattern is used to capture the internal state of an object and store it in a way that allows the object to be restored to that state later. It is often used to implement features such as undo/redo, checkpointing, and serialization in software applications.

The main purpose of using the Memento design pattern is to provide a way to restore an object to a previous state without violating encapsulation. By using the Memento pattern, you can ensure that the internal state of an object is not exposed to other objects and can only be modified through the object’s public interface. This helps to maintain the integrity of the object’s internal state and makes it easier to modify the object’s implementation without breaking existing code that depends on it.

Here are a few scenarios where the Memento design pattern might be useful:

  1. Implementing an undo/redo feature in a software application. By using the Memento pattern, you can store a snapshot of the application’s state each time the user makes a change, and then provide a way to restore the application to a previous state by using a Memento object.
  2. Creating a checkpoint system in a computer game. The Memento pattern can be used to store the state of the game at a particular point in time, allowing the player to restore the game to a previous checkpoint if they die or make a mistake.
  3. Persisting the state of an object between sessions in a web application. By using the Memento pattern, you can store the state of an object in a way that can be easily serialized and deserialized, allowing the object to be restored to its previous state when the application is restarted.
  4. Implementing a state machine. The Memento pattern can be used to store the state of a state machine at a particular point in time, allowing the machine to be restored to a previous state if necessary.
  5. Implementing a version control system for a document management system. The Memento pattern can be used to store a snapshot of a document’s state each time it is modified, allowing the document to be restored to a previous version if necessary.

Thanks for reading! If you found the article helpful, you can clap and follow. So you will notified of new articles.

# Reference

It was created with the help of ChatGPT AI.

--

--

Göksu Deniz
Göksu Deniz

Written by Göksu Deniz

Software Engineer, passionate about creating efficient solutions. Skilled in mentoring teams to deliver successful projects. Always exploring new tech trends.