I regularly teach courses on basic object-oriented design patterns. Over the years I have come to realize that not every pattern is equally useful or equally well-thought out. One of the most common examples of patterns that have become more like anti-patterns over the years is the Singleton pattern. But this post is not about that one, but another that is a bit odd: the Memento.

Defining the pattern

The Memento design pattern makes it possible to save the state of an object (the Originator) into what are basically snapshots (the Mementos) of its internal state. These mementos are handled by the Caretaker and can be later used to restore the state of the Originator.

Problem with this pattern

It's easy to see that (although the original definition explicitly adds that 'without violating encapsulation') it violates encapsulation. When you create a means of injecting complete state information into your objects from the outside, you always risk your object being compromised. I mean, the usual implementation goes like this:

public class Originator
{
   public Memento CreateMemento()
   {
      return new Memento(); // TODO: Add state to memento here
   }
   
   public void SetFromMemento(Memento m)
   {
     // TODO: Restore from memento here
   }
}

public class Memento
{
  // TODO: Store state here
}

Let's look at some hard facts:

  • The state must be saved by clients of the Originator, so both methods must be available through the public interface. And as part of the interface, the Memento type must also be available.
  • And of course, the Mementos are handled by the clients, so they have to be public also.

These are hard requirements that make it hard to implement the pattern correctly. With this naive implementation, anyone can just create a Memento and inject it into the Originator. That's bad :) A modified version looks like this:

public class Originator
{
   public IMemento CreateMemento()
   {
      return new Memento(); // TODO: Add state to memento here
   }
   
   public void SetFromMemento(IMemento m)
   {
     // TODO: Restore from memento here
   }
}

internal class Memento : IMemento
{
  // TODO: Store state here
}

public interface IMemento
{
  
}

This also adheres to the hard requirements: the mementos are still publicly accessible, but they are hidden behind an interface and the actual implementation is internal. So now coders can't just new up mementos in code wherever they want. Or can they?

Well, they can, of course. They just have to create their own implementation of the interface (remember, the interface has to be public). So you have to somehow make sure that only your implementation is accepted by the Originator. Remember, you cannot change the interface of the Originator and include the internal type, because that method must be public. So what do you do? Well, you probably violate every basic object-oriented principle there is like this:

public void SetFromMemento(IMemento m)
{
  if (m is not Memento)
    throw new ArgumentException("Cannot accept external memento implementations");
  // TODO: Restore from memento here
}

Solving the problem the OO way

During one of my courses when we were brainstorming with the participants about how to solve this problem (yes, I do brainstorm with them, because they actually are software developers and have good ideas), one of them came up with a compile time solution that I actually liked to prevent developers from creating their own memento implementaitons. First, use an abstract class and not an interface. And then, just hide the constructor (actually, their solution was to create an internal abstract method that obviously cannot be overridden in any descendant class outside the assembly, preventing you from creating a new-able memento type, but the idea is the same):

public abstract class MementoBase
{
  internal MementoBase()
  {
  }
}

This prevents any developer from creating descendants and implementing their own types outside of the actual memento assembly. And this way you don't have to add that nasty runtime type-check to SetMemento(), because there are no external mementos.

Problem solved?

No, not really. As I pointed out to them, there is one other problem that this does not solve: you can create multiple Originators, create mementos for each and then mix and match the mementos when restoring them. This way you can inject the state of one Originator into another, and that's probably not good.

If you have a solution to this problem, feel free to share it in the comments, but as far I can see, there isn't any. That's way I usually implement this pattern in a very different way, storing the Mementos inside the Originator and returning only a reference value to the Memento:

public class Originator
{
  private readonly Dictionary<Guid,Memento> mementos = new Dictionary<Guid,Memento>();
  
  public Guid CreateMemento()
  {
    Guid key = Guid.NewGuid();
    mementos.Add(key, new Memento());
    return key;
  }
  
  public void SetFromMemento(Guid mementoKey)
  {
    var memento = mementos[mementoKey];
    // TODO: restore state
  }
}

And now, since every Origintor has explicit knowledge about the Mementos created for them, there is no way to mix and match Mementos and Originators. Quite different from the original pattern, but I think this does adhere to encapsulation better.

Memento design pattern done (almost) right
Share this

© 2018. All Rights Reserved.

Falconium theme based on Ghostium Theme

Proudly published with Ghost