Memento Pattern

Greetings, fellow developers! Welcome back to .Net Core Central. Today, we embark on a journey into the realm of design patterns, focusing our attention on the Memento Design Pattern – a gem among the Gang of Four design patterns.

Understanding the Memento Design Pattern

The Memento Design Pattern, a member of the behavioral design patterns family, serves a crucial role in capturing and externalizing an object’s internal state. Its primary objective is to enable the restoration of an object to a specific state without violating encapsulation.

To comprehend this pattern, let’s consider scenarios such as video games, employee management systems, or even text editors where the ability to capture and restore states becomes invaluable.

Real-world Scenarios

Imagine playing a multi-state video game, where saving the game state allows users to roll back to a specific point rather than starting anew. Similarly, in a word processing application, the option to undo a written statement can be attributed to the Memento pattern. In the context of an Employee Management System, it proves handy when correcting erroneous data entries.

Implementation of Memento Pattern in .NET Core

To illustrate the workings of the Memento Design Pattern, we’ll dive into a scenario involving an HR system. The system manages employee information, and we’ll demonstrate how to externally manage and restore the state of employees without compromising encapsulation.

Key Components:

Employee Class:

  • Properties: Name, Age, Phone
  • Methods:
    • Create: Captures the current state and returns a Memento object.
    • Undo: Restores the state to a specified Memento.
namespace MementoPattern.Demo;

internal interface IEmployee
{
    int Id { get; }
    int Age { get; set; }
    string Name { get; set; }
    string Phone { get; set; }

    Memento Create();
    void Undo(Memento memento);
}

internal class Employee : IEmployee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Phone { get; set; }

    public Memento Create() => new Memento(Id, Name, Age, Phone);

    public void Undo(Memento memento)
    {
        Id = memento.Id;
        Name = memento.Name;
        Age = memento.Age;
        Phone = memento.Phone;
    }
}

Memento Class:

  • Properties: Name, Age, Phone

namespace MementoPattern.Demo;

internal record Memento(int Id, string Name, int Age, string Phone);

EmployeeManager Class:

  • Responsibilities: Manages and stores Memento objects in an in-memory stack.
  • Methods:
    • AddMemento: Adds a Memento to the stack.
    • GetMemento: Retrieves the last Memento from the stack.
namespace MementoPattern.Demo;

internal interface IEmployeeManager
{
    void AddMemento(int id,Memento memento);
    Memento GetMemento(int id);
}

internal class EmployeeManager : IEmployeeManager
{
    private readonly Dictionary<int, Stack<Memento>> mementos = new Dictionary<int, Stack<Memento>>();

    public void AddMemento(int id, Memento memento) 
    {
        if (!mementos.ContainsKey(id)) mementos.Add(id, new Stack<Memento>());
        mementos[id].Push(memento); 
    }

    public Memento GetMemento(int id) => mementos[id].Pop();
}

Putting it into Action

In our example, we create an employee, modify their state, and then utilize the Memento pattern to undo changes. The EmployeeManager class stores the Mementos in an in-memory stack, but in a real-world scenario, this could involve external data stores.

using MementoPattern.Demo;

var employee = new Employee { Id =1, Name = "Employee", Age = 25, Phone = "123" };
var employeeManager = new EmployeeManager();

employeeManager.AddMemento(1,employee.Create());

Console.WriteLine($"Initial State: {employee.Name}, {employee.Age}, {employee.Phone}");

employee.Phone = "34567";
employeeManager.AddMemento(1, employee.Create());
Console.WriteLine($"{employee.Name}, {employee.Age}, {employee.Phone}");

employee.Age = 35;
employeeManager.AddMemento(1, employee.Create());
Console.WriteLine($"{employee.Name}, {employee.Age}, {employee.Phone}");

employee.Undo(employeeManager.GetMemento(1));
employee.Undo(employeeManager.GetMemento(1));
Console.WriteLine($"{employee.Name}, {employee.Age}, {employee.Phone}");

employee.Undo(employeeManager.GetMemento(1));
Console.WriteLine($"{employee.Name}, {employee.Age}, {employee.Phone}");

Conclusion

The Memento Design Pattern, by adhering to SOLID principles, elegantly addresses scenarios where undo operations or state restoration are critical. Each class has a well-defined responsibility, promoting maintainability and extensibility.

That wraps up our exploration of the Memento Design Pattern in .NET Core. If you found this walkthrough insightful, don’t forget to go through the rest of my blog posts. For more engaging content, consider going to my YouTube channel and subscribing to .Net Core Central. Thank you for joining me in unraveling the intricacies of design patterns!