Memento Pattern in C# : Unlocking the Past | by Softinbit | May, 2024 | Towards Dev

Memento Pattern in C# : Unlocking the Past

Softinbit
Towards Dev
Published in
4 min readMay 2, 2024

--

Remembering the past doesn’t require a time machine, just a Memento!

🔍Attention:

Before we start talking about design patterns, it’s important to be clear about what you can expect. These posts are like treasure maps, showing you where the gold is buried, but not handing you the shovel.

We’ll give you a peek into the world of design patterns, but we won’t give you all the instructions for building something right away. Instead, we’ll explain the ideas behind them.

Stay tuned for the next blog post where we’ll show you how to put these ideas into action. Until then, enjoy the journey and keep your eyes open for more insights! 🔍

Imagine you’re playing a game and you’ve reached a crucial point, but suddenly your progress is lost. WTF, right? Within the domain of software development, losing crucial data can be just as maddening. Luckily, there’s a design pattern that acts as a safeguard against such mishaps — the Memento pattern.

How to Implement in the code ?

Implementing the Memento pattern involves three main components: the Originator, the Memento, and the Caretaker.

The Originator is the object whose state needs to be saved and restored. The Memento is an immutable object that stores the state of the Originator. The Caretaker is responsible for storing and restoring the Originator’s state using Mementos.

Consider a text editor application where users can undo and redo their actions. The text editor (Originator) keeps track of its state using Mementos, which store snapshots of the text. The Caretaker manages these Mementos, allowing users to undo and redo their actions seamlessly.

public class TextEditor
{
private string text;

public string Text
{
get => text;
set => text = value;
}

public TextMemento Save()
{
return new TextMemento(text);
}

public void Restore(TextMemento memento)
{
text = memento.Text;
}
}

public class TextMemento
{
public string Text { get; }

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

public class TextEditorCaretaker
{
private readonly Stack<TextMemento> mementos = new Stack<TextMemento>();

public void Save(TextMemento memento)
{
mementos.Push(memento);
}

public TextMemento Restore()
{
return mementos.Pop();
}
}

Real-World Analogy

Think of the Memento pattern like saving checkpoints in a video game. When you reach a significant milestone, you save your progress so that you can revert to it later if needed.

Applicability

The Memento pattern is useful in situations where you need to save and restore the state of an object, such as undo mechanisms, transaction rollback systems, and history tracking features.

Pros and Cons

Pros:

  • Allows for flexible state management without exposing internal details.
  • Supports undo/redo functionality easily.
  • Encourages separation of concerns by delegating state management to separate objects.

Cons:

  • Can consume significant memory if Mementos are large or numerous.
  • Overuse of Mementos can lead to complex code and reduced performance.

Problems it Solves

The Memento pattern addresses the problem of preserving and restoring an object’s state without coupling it to the system’s implementation details. It enables applications to implement features like undo/redo functionality and state recovery with ease, enhancing user experience and data integrity.

Let’s see some examples

You’re tasked with developing an inventory management system for a warehouse. You need to ensure that the system can handle inventory updates efficiently while also providing a way to revert changes if necessary. The Memento pattern comes in handy for this scenario.

// Originator: Represents the inventory item
public class InventoryItem
{
public string Name { get; set; }
public int Quantity { get; set; }

public InventoryMemento Save()
{
return new InventoryMemento(Name, Quantity);
}

public void Restore(InventoryMemento memento)
{
Name = memento.Name;
Quantity = memento.Quantity;
}
}

// Memento: Stores the state of the inventory item
public class InventoryMemento
{
public string Name { get; }
public int Quantity { get; }

public InventoryMemento(string name, int quantity)
{
Name = name;
Quantity = quantity;
}
}

// Caretaker: Manages the inventory item's state history
public class InventoryCaretaker
{
private Stack<InventoryMemento> mementos = new Stack<InventoryMemento>();

public void Save(InventoryMemento memento)
{
mementos.Push(memento);
}

public InventoryMemento Restore()
{
return mementos.Pop();
}
}

Let’s see another example

Suppose you’re developing a text editor application that allows users to undo and redo their actions. You want to ensure that users can revert changes made to the text seamlessly. The Memento pattern facilitates this functionality.

// Originator: Represents the text editor
public class TextEditor
{
private string text;

public string Text
{
get => text;
set => text = value;
}

public TextMemento Save()
{
return new TextMemento(text);
}

public void Restore(TextMemento memento)
{
text = memento.Text;
}
}

// Memento: Stores the state of the text editor
public class TextMemento
{
public string Text { get; }

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

// Caretaker: Manages the text editor's state history
public class TextEditorCaretaker
{
private Stack<TextMemento> mementos = new Stack<TextMemento>();

public void Save(TextMemento memento)
{
mementos.Push(memento);
}

public TextMemento Restore()
{
return mementos.Pop();
}
}

The last example :

Consider a financial transaction system where users can perform various transactions like deposits, withdrawals, and transfers. It’s crucial to maintain the integrity of financial data and provide a way to rollback transactions if needed. The Memento pattern facilitates this requirement.

// Originator: Represents a financial transaction
public class Transaction
{
public decimal Amount { get; set; }
public DateTime Timestamp { get; set; }

public TransactionMemento Save()
{
return new TransactionMemento(Amount, Timestamp);
}

public void Restore(TransactionMemento memento)
{
Amount = memento.Amount;
Timestamp = memento.Timestamp;
}
}

// Memento: Stores the state of the financial transaction
public class TransactionMemento
{
public decimal Amount { get; }
public DateTime Timestamp { get; }

public TransactionMemento(decimal amount, DateTime timestamp)
{
Amount = amount;
Timestamp = timestamp;
}
}

// Caretaker: Manages the financial transaction's state history
public class TransactionCaretaker
{
private Stack<TransactionMemento> mementos = new Stack<TransactionMemento>();

public void Save(TransactionMemento memento)
{
mementos.Push(memento);
}

public TransactionMemento Restore()
{
return mementos.Pop();
}
}

--

--

Custom software solutions for your business. Driving growth and innovation through the power of technology. | info@softinbit.com | www.softinbit.com

Recommended from Medium

Lists

See more recommendations