Click here to Skip to main content
Click here to Skip to main content

Mementos

, 2 Jun 2005
Rate this:
Please Sign up or sign in to vote.
A basic IMemento implementation.

There is no download associated with this article. For some usage examples, check out:

Introduction

The IMemento interface is simple yet powerful. Classes that implement this interface can preserve and restore their state in any internal representation that they choose. Classes that interface to classes implementing IMemento can request that the instance save/restore its state without knowing the details of the save/restore mechanism. While this can deceive you into thinking that IMemento is a good way of managing persistent information, it is not. For that, the usual n-tiered pattern, which usually includes a data access layer, should be used. The IMemento is useful in storing localized state information regarding an object. This state information is disassociated with any other application information (as in, it does not have any relationship to any other information in the application).

Memento Design Pattern

The Memento design pattern is well known and you can read more about it here. The Eclipse project also has a good write-up on it here. One might not think that the Memento design pattern is worthy of an entire article, but I feel that it is such a useful pattern that it can stand on its own merits. Furthermore, this article describes extending the Memento design pattern to something that I think is more useable for real world applications. As the Memento design pattern stands, it is lacking some features.

The typical design pattern looks like this:

The reality is though, that one rarely uses a memento without also associating some contextual information about the memento. Why was the memento created? What did the user just do? What did the application just do? For this reason, I find it more useful to have the Memento object include an Action string:

If you read the documentation on the Memento design pattern, you'll notice that there is an "Originator" class that implements SetMemento and CreateMemento methods. For this mechanism to be truly useful, the class where you want to preserve/restore state information should ideally tell the application that it is capable of doing so, through its own interface, which I have called "ISupportMemento":

Now we have all the players on the stage--classes that support mementos always implement a standard interface mechanism, and the classes that implement the memento include some contextual information.

Interface Implementations

public interface IMemento
{
  object State
  {
    get;
    set;
  }

  string Action
  {
    get;
    set;
  }
}

public interface ISupportMemento
{
  IMemento Memento
  {
    get;
    set;
  }
}

A Concrete Memento Class

If you don't have to deal with multiple inheritance issues, you can derive your specialized memento class from a concrete Memento class rather than an interface. Usually this should be the case, since the memento is managed by a separate class rather than the originator class. The basic implementation looks like this:

public class Memento : IMemento
{
  protected object state;
  protected string action;

  public virtual object State
  {
    get {return state;}
    set {state=value;}
  }

  public string Action
  {
    get {return action;}
    set {action=value;}
  }
}

You will note that I am using properties instead of discrete GetState/SetState methods. Since the design patterns were originally written for C++, they don't always look like what you'd probably want to implement in C#. So, my actual IMemento interface looks like this, diagrammatically:

Who Manages State?

Note that there's quite a bit of flexibility with regards to which object creates the state information. I've illustrated two slightly different implementations. In this example, the class implementing ISupportMemento creates the state:

Example 1: Originator Creates The State Object

public class MyClass : ISupportMemento
{
  public IMemento Memento
  {
    get
    {
      Memento mcm=new Memento();
      mcm.State=GetMyState();
      return mcm;
    }
    set
    {
      SetMyState(value.State);
    }
  
  protected object GetMyState()
  {
    // ... implementation ...
  }

  protected void SetMyState(object state)
  {
    // ... implementation ...
  }
}

In this example, the class implementing the IMemento creates the state.

Example 2: Memento Creates The State Object

public class MyClass : ISupportMemento
{
  public IMemento Memento
  {
    get
    {
      IMemento mcm=new MyClassMemento(this);
      return mcm;
    }
    set
    {
      ((MyClassMemento)value).SetState(this);
    }
  }
}

public class MyClassMemento : Memento
{
  public MyClassMemento(MyClass mc)
  {
    // get MyClass state by querying properties of MyClass instance.
  }

  public void SetState(MyClass mc)
  {
    // set MyClass state by setting properties of MyClass instance.
  }
}

The advantage with the second implementation is that you can change the persistence mechanism without affecting the originator implementation--it is independent of the originator class. However, the implementation is a bit more complex and relies on casting the IMemento to the appropriate concrete memento implementer, which can be prone to error if the wrong instance is passed in.

Conclusion

I specifically wanted to avoid the topic of "how do you actually create the state?" There are many answers--maybe the state is a simple value. Maybe you want to use XML serialization, or binary serialization. In the first example, the originator has the smarts to persist itself. In the second example, you may want to decorate properties with an attribute that provides information about what properties should be persisted. You may want to include default values. So, as you can see, the issue of persistence is itself complex.

One final note--you'll find the memento pattern used frequently in undo/redo buffers, and this is why I've also included the Action contextual information in my IMemento implementation.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Marc Clifton

United States United States
Marc is the creator of two open source projets, MyXaml, a declarative (XML) instantiation engine and the Advanced Unit Testing framework, and Interacx, a commercial n-tier RAD application suite.  Visit his website, www.marcclifton.com, where you will find many of his articles and his blog.
 
Marc lives in Philmont, NY.

Comments and Discussions

 
GeneralThis is not Memento PinmemberFrank Szendzielarz3-Nov-05 23:21 
GeneralRe: This is not Memento [modified] PinmemberPunCha22-Nov-06 21:56 
GeneralMemento Strategy PinmemberBosah27-Jun-05 9:19 
GeneralPotential for casting exception in example 1 Pinmembertommyv10-Jun-05 4:08 
the [second] implementation is a bit more complex and relies on casting the IMemento to the appropriate concrete memento implementer, which can be prone to error if the wrong instance is passed in.
 
Wouldn't the first implementation be prone to a similar error, just in a different place. In the first example, the SetMyState method will need to cast the state parameter to a type that it can work with in order to actually extract the state. If the momento was created by an incompatible class then the cast will be invalid.
GeneralMemento Hides State PinmemberSamuel Ford7-Jun-05 1:13 
GeneralPoint Pinmembernorm.net2-Jun-05 21:32 
GeneralRe: Point PinprotectorMarc Clifton3-Jun-05 0:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140709.1 | Last Updated 2 Jun 2005
Article Copyright 2005 by Marc Clifton
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid