Memento Pattern

The Memento pattern is a classic behavioral design pattern used to capture the internal state of an object without violating its encapsulation. It allows you to save the state of an object and later restore it. The Memento pattern is useful in many scenarios, such as implementing undo/redo functionality, saving and restoring the state of an object, or saving the state of an object to a file.

As the Memento pattern involves a lot of boilerplate code, it is a good candidate for automatic creation by Metalama.

Example

In the following example, the [Memento] custom attribute is all you need to implement the Memento pattern on the Fish class.

[Memento]
public sealed partial class Fish
{
    public string? Name { get; set; }
    public string? Species { get; set; }
    public DateTime DateAdded { get; set; }
}

The aspect implements the IMementoable interface, including its SaveToMemento and RestoreMemento methods.

var fish = new Fish() { Name = "Hannibal", Species = "Tilapia" };

// Save
var memento = fish.SaveToMemento();

// Change
fish.Tilapia = "Shark";

// Undo change
fish.RestoreMemento(memento);

Show me how it works!

The aspect, once applied, will create an internal Memento class to capture the state of the Fish class. The Fish class will also have a SaveToMemento method and a RestoreMemento method to save and restore the state of the Fish object.

public partial class Fish : IMementoable
{
  public void RestoreMemento(IMemento memento)
  {
    var typedMemento = (Memento) memento;
    this.Name = typedMemento.Name;
    this.Species = typedMemento.Species;
    this.DateAdded = typedMemento.DateAdded;
  }

  public IMemento SaveToMemento()
   => new Memento(this);

  private class Memento: IMemento
  {
    public Memento(Fish originator)
    {
      this.Originator = originator;
      this.Name = originator.Name;
      this.Species = originator.Species;
      this.DateAdded = originator.DateAdded;
    }

    public string? Name { get; }
    public string? Species { get; }
    public DateTime DateAdded { get; }
    public IMementoable? Originator { get; }
  }
}

You can find the complete Memento aspect source code here.

Metalama benefits

  • Increase your productivity: The pattern requires a lot of boilerplate code. Instead of writing it manually, an aspect can handle it for you, allowing you to focus exclusively on your business logic.
  • Keep your code consistent: The generated code is always consistent with the pattern rules.
  • Enhance maintainability: The generated code will always be up-to-date; you’ll never forget to update the Memento class when you add a new property to the Mementoable class.

Resources