Memento Design Pattern

Memento Design Pattern

The Memento Design Pattern is a classic behavioral pattern used in software development, particularly effective when you want to save and restore the state of an object without exposing its internal structure. In this article, we’ll dive deep into the Memento pattern, explore a practical Bank Account example in C#, and provide insights on how .NET developers can leverage this pattern for real-world applications.

Table of Contents

  1. Introduction to the Memento Design Pattern
  2. Real-World Analogy: Undo in Word Processors
  3. Structure of the Memento Pattern in C#
  4. The Bank Account Example
  5. Benefits and Limitations
  6. Final Thoughts and Best Practices

1. Introduction to the Memento Design Pattern

In software systems, managing the state of an object is a common need. Whether it's implementing an Undo/Redo feature, preserving snapshots in workflows, or creating state history for simulations, being able to roll back to a previous state is invaluable.

The Memento Design Pattern solves this elegantly by allowing objects to save and restore their state without violating encapsulation. It does this using three core components:

  • Originator: The object whose state needs to be saved and restored.
  • Memento: The snapshot object that stores the state.
  • Caretaker: Manages the history and provides restore functionality.

2. Real-World Analogy: Undo in Word Processors

Think of Microsoft Word. Every time you type, Word silently stores a snapshot of the document. When you press Ctrl+Z, it restores the previous snapshot. Word (the Originator) generates Mementos, which are stored by the Caretaker (a history stack). This non-intrusive snapshot-and-restore mechanism is precisely what the Memento pattern offers.


3. Structure of the Memento Pattern in C#

Here’s a simplified structural overview in C#:

// Memento
public class Memento
{
    public string State { get; }
    public Memento(string state) => State = state;
}

// Originator
public class Originator
{
    private string _state;

    public void SetState(string state) => _state = state;
    public string GetState() => _state;
    public Memento Save() => new Memento(_state);
    public void Restore(Memento memento) => _state = memento.State;
}

// Caretaker
public class Caretaker
{
    private Stack<Memento> _history = new();

    public void Backup(Originator originator) => _history.Push(originator.Save());
    public void Undo(Originator originator)
    {
        if (_history.Count > 0)
            originator.Restore(_history.Pop());
    }
}

This separation ensures that only the Originator knows how to save and restore its internal state, promoting encapsulation and loose coupling.


4. The Bank Account Example

Let’s bring the concept to life using a Bank Account simulation in C#.

Scenario

Imagine a bank account that allows deposits, withdrawals, and undoing operations — each financial change should be reversible.

C# Implementation

BankAccount (Originator)

public class BankAccount
{
    private int _balance;

    public BankAccount(int balance) => _balance = balance;

    public Memento Save() => new Memento(_balance);
    public void Restore(Memento memento) => _balance = memento.Balance;

    public void Deposit(int amount) => _balance += amount;
    public bool Withdraw(int amount)
    {
        if (amount > _balance) return false;
        _balance -= amount;
        return true;
    }

    public override string ToString() => $"Balance: {_balance}";

    public class Memento
    {
        public int Balance { get; }

        public Memento(int balance) => Balance = balance;
    }
}

Caretaker (TransactionHistory)

public class TransactionHistory
{
    private Stack<BankAccount.Memento> _history = new();

    public void SaveState(BankAccount account) => _history.Push(account.Save());

    public void Undo(BankAccount account)
    {
        if (_history.Count > 0)
            account.Restore(_history.Pop());
    }
}

Usage Example

var account = new BankAccount(1000);
var history = new TransactionHistory();

Console.WriteLine(account);  // Balance: 1000

history.SaveState(account);
account.Deposit(500);
Console.WriteLine(account);  // Balance: 1500

history.SaveState(account);
account.Withdraw(200);
Console.WriteLine(account);  // Balance: 1300

history.Undo(account);
Console.WriteLine(account);  // Balance: 1500

history.Undo(account);
Console.WriteLine(account);  // Balance: 1000

Why It Works

This example demonstrates transactional control, similar to real banking systems. In the real world, this concept underpins features like rollback in databases or state checkpoints in financial systems.


5. Benefits and Limitations

✅ Benefits

  • Encapsulation is preserved: No external class can directly modify the internal state.
  • Undo/Redo functionality: Perfect for editors, games, or form wizards.
  • State isolation: Each Memento is a clean snapshot, avoiding side effects.

❌ Limitations

  • Memory overhead: If state objects are large or frequent, memory use increases.
  • Serialization challenge: If state involves complex or reference-heavy objects, you may need deep copying or object graphs.
  • No automatic garbage collection for unused mementos: Without careful management, history stacks may grow unchecked.

Pro Tip: For large systems, consider integrating the Memento pattern with Prototype pattern (for deep copying) or use Serialization frameworks for efficient state management.


When to Use It:

  • Undo/Redo functionality in forms or editors.
  • Rollback mechanisms in services or business logic.
  • State saving for workflows, wizard steps, or simulations.

Summary

The C# Memento Design Pattern enables developers to capture and restore an object’s internal state without breaking encapsulation. It’s practical, especially for building robust and reversible business logic in C# and .NET applications. By understanding and applying this pattern properly, you can deliver highly reliable, user-friendly systems that gracefully handle mistakes, rollbacks, or dynamic user interactions.

Subscribe to arash.ninja

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
[email protected]
Subscribe