The Memento Pattern is a software design pattern that is used to save and restore objects to their original states. The purpose of the Memento Pattern is to enable effortless restoration of an object's previous state. In this article, we'll be looking at the Memento Pattern in C# with code examples.
In software engineering, state restoration is the ability to bring back the previous states of an object or system. This is important because it helps prevent system crashes and loss of data. With the Memento Pattern in C#, developers can implement state restoration without much effort. This design pattern is particularly useful when building applications that need to track changes to an object over time.
By following the steps outlined in this article, C# developers can easily implement the Memento Pattern in their C# projects.
What's In This Article: The Memento Pattern in C#
Remember to check out these other platforms:
// FIXME: social media icons coming back soon!
How the Memento Pattern Works
The Memento Pattern is a software design pattern that allows developers to restore an object's previous state with ease. It is often used in undo-redo systems or situations requiring state restoration -- and we're all familiar with pressing ctrl+Z to get our work back! It works by separating an object's state and creating a memento object that stores the state. This memento object can later be returned to restore the object's previous state.
Origin and Principle of the Memento Pattern
Design patterns are generally classified into three categories: behavioral, creational, and structural patterns. The Memento Pattern falls under the behavioral pattern category. It was first introduced by the GoF (Gang of Four) in their book "Design Patterns: Elements of Reusable Object-Oriented Software" (affiliate link).
The principle of the Memento Pattern is to save and restore an object's previous state without exposing its private implementation. This pattern ensures encapsulation and separation of concerns by separating an object's state from the object itself. This way, the object's state can be retrieved and restored without compromising its integrity.
Different Components of the Memento Pattern in C#
The primary components of the Memento Pattern in C# include the Memento, Originator, Details, and Caretaker. The Memento object stores the object's state, while the Originator creates and restores the object's state. Details provide a snapshot of the object state, and Caretaker is responsible for the object's history.
The Memento object captures and saves the current state of the Originator object. The Originator object creates a new Memento object and saves its state in the Memento object. The Details object is used to provide a snapshot of the object state. Caretaker is responsible for saving and restoring the object's state history. We'll see this in more detail by looking at some code momentarily!
Examples of the Memento Pattern
One use case example for the Memento Pattern is for a game that allows the save and load of levels. A game level can be designed as an object, and its state, including objects placed, scores gained, and player location, can be captured and saved in a Memento object. The system can later load and restore the saved level state.
Another example of where the Memento Pattern can be applied is in a text editor system (just like the code editor you use daily). In such a system, users can perform undo and redo operations after typing. The Memento Pattern can be used to store a snapshot of the text area's state. When an undo operation is performed, the text area can be restored to its previous state, as stored in the memento.
[[oceanwp_library_5654]]
Steps to Implement Memento Pattern in C#
Creating the Originator Class
The first step is to create the Originator class, which is responsible for creating and restoring an object's state. The Originator class needs to have a method that returns a Memento object containing the current state of the object.
To create an Originator class, you'll first need to define the state you want to save as properties. Then, you'll need to create a method that returns a new Memento object with the current state. This method should save the current state of the properties in a new Memento object.
Below is an example of a simple Originator class that saves and restores a string property:
public sealed class TextEditor
{
private string text;
public string Text
{
get => text;
set
{
text = value;
Save();
}
}
private void Save()
{
Console.WriteLine($"Saving state: {text}");
}
public TextEditorMemento CreateMemento() => new TextEditorMemento(text);
public void Restore(TextEditorMemento? memento)
{
text = memento?.Text ?? string.Empty;
Console.WriteLine($"State restored: {text}");
}
public class TextEditorMemento
{
public string Text { get; private set; }
public TextEditorMemento(string text)
{
Text = text;
}
}
}
The Memento Class
The next step in implementing the Memento Pattern is creating a Memento class. The Memento class stores the state of the Originator object and provides access to that state when the object's state needs to be restored.
To create a Memento class, you'll need to define the state you want to save as properties. Then, you'll need to create a constructor that takes in the object's current state and sets the state properties.
Below is an example of a simple Memento class for our TextEditor:
public sealed class TextEditorMemento
{
public string Text { get; private set; }
public TextEditorMemento(string text)
{
Text = text;
}
}
The Caretaker Class
The third step in implementing the Memento Pattern is creating the Caretaker class. The Caretaker class is responsible for storing and managing the Memento objects that hold the Originator object's state. It provides an interface for saving and restoring the object state.
To create a Caretaker class, you'll need to define a list for storing the Memento objects. Then, you'll need to create methods for adding Mementos to the list and retrieving Mementos from the list.
Here's a simple Caretaker class for our TextEditor example:
public sealed class TextEditorHistory
{
private readonly Stack<TextEditorMemento> _mementos = new Stack<TextEditorMemento>();
public TextEditorMemento? Current => _memento.Count > 0
? _memento.Peek()
: null;
// Saves the state to become the current state
public void Save(TextEditorMemento memento)
{
_mementos.Push(memento);
Console.WriteLine($"Editor state saved: {memento.Text}");
}
// Returns the state that was "undone"
public TextEditorMemento? Undo()
{
if (_mementos.Count > 0)
{
var memento = _mementos.Pop();
Console.WriteLine($"Editor state restored: {memento.Text}");
return memento;
}
return null; // Or a default memento
}
}
Adding Undo Functionality
The final step is to add 'Undo' functionality. When 'Undo' is performed, the latest Memento object is retrieved from the Caretaker object and used to restore the object's state. The Originator object is updated with the restored state.
To add 'Undo' functionality, you'll need to modify the Originator class to include a method that restores the object's state from a Memento object. Then, you'll need to modify the Caretaker class to keep track of a history of Mementos so that the application can undo changes.
Here's an example of a simple 'Undo' implementation for our TextEditor example:
var textEditor = new TextEditor();
var history = new TextEditorHistory();
textEditor.Text = "hello";
history.Save(textEditor.CreateMemento());
textEditor.Text = "world";
history.Save(textEditor.CreateMemento());
// Removes the "world" state
history.Undo();
// Restores state to "hello"
textEditor.Restore(history.Current);
When we call Undo(), we have modified the history. However, we still need to provide the current history to the text editor.
Benefits and Tradeoffs
Benefits of the Memento Pattern in C#
Implementing the Memento Pattern in C# can bring several benefits to your code. First, it can simplify the application's state restoration process. The Memento Pattern allows objects to be restored to their previous state with minimal complexity or interference. It can also improve an application's scalability and flexibility since it separates an object's state and its implementation.
Another benefit is that it can improve the code's readability and maintainability by organizing the necessary code into separate components. The Memento Pattern meets the 'Single Responsibility Principle' and encapsulates all of the necessary information regarding the object's state. Compared to some other design patterns, the Memento Pattern is more maintainable, scalable, and flexible.
Drawbacks of the Memento Pattern in C#
Like any other software design pattern, the Memento Pattern has drawbacks. Implementing the pattern requires an increase in memory since the Memento objects use more memory to store states. This increase can pile up, resulting in performance degradation -- So you need to be careful when managing state. It might be a good opportunity for some tests!
In addition, Memento Pattern can overcomplicate simple applications. When deciding whether to use the Memento Pattern or another software design pattern, it is essential to consider the application's use case. The Memento Pattern is most useful when an object or application undergoes frequent state changes. The pattern would not be recommended for systems with minimal state changes.
In general, the Memento Pattern is an excellent software design pattern that can bring valuable benefits to your code. It can also help an application scale by separating the aspects of state management from an object's implementation. Still, before implementation, it is essential to consider the tradeoffs and how it can affect the application's overall performance.
Wrapping Up The Memento Pattern in C#
In this article, I discussed the Memento Pattern in C# and how it can be used to restore the state of an object effortlessly. We covered the origin and principle of the Memento Pattern and explained its different components, including the Memento, Originator, Details, and CareTaker classes.
I also provided a detailed explanation of the steps required to implement the Memento Pattern in C#, including creating an Originator class, a Memento class, and a Caretaker class. I also discussed how to add 'Undo' functionality to the pattern and the benefits and tradeoffs of implementing it in your programs.
In conclusion, using the Memento Pattern in C# can lead to easy state restoration and provide a cleaner, more organized codebase. If you're interested in more learning opportunities, subscribe to my free weekly newsletter and check out my YouTube channel!
Affiliations
These are products & services that I trust, use, and love. I get a kickback if you decide to use my links. There’s no pressure, but I only promote things that I like to use!
- BrandGhost: My social media content and scheduling tool that I use for ALL of my content!
- RackNerd: Cheap VPS hosting options that I love for low-resource usage!
- Contabo: Affordable VPS hosting options!
- ConvertKit: The platform I use for my newsletter!
- SparkLoop: Helps add value to my newsletter!
- Opus Clip: Tool for creating short-form videos!
- Newegg: For all sorts of computer components!
- Bulk Supplements: Huge selection of health supplements!
- Quora: I answer questions when folks request them!