Tuesday, December 1, 2009

Design Patterns – Memento Pattern

The Memento Pattern is useful to remember the state of object without keeping copy of the whole object. Copying of the entire object is sometimes inefficient as the copy eventually contains much more information that we need to restore back.



Memento



Memento is the object that can remember the internal state of another object.


// Memento. Uses deep copy and serialization
// to save/restore the state of type TState.
public class Memento<TState>
{
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();

public Memento<TState> Save(TState state)
{
formatter.Serialize(stream, state);
return this;
}

public TState Restore()
{
stream.Seek(0, SeekOrigin.Begin);
TState state = (TState)formatter.Deserialize(stream);
stream.Close();
return state;
}
}



The Memento in this example uses generics to ensure type consistence and prevent using type conversion.

Originator



Originator is the object which state to remember and restore back if needed. The Originator has two methods for that:
- GetMemento() that returns the state of the object
- and SetMemento() receives back state to restore from


// The Originator has one property that returns
// the complete state of the object.
public interface IOriginator<TState>
{
TState State { get; }
}

// Holds the state of the Originator
[Serializable]
public class ProductState
{
public string Name { get; set; }
public float Price { get; set; }
}

// Originator
// The complete state is moved to one property
// to generalize the memento pattern.
[Serializable]
public class Product : IOriginator<ProductState>
{
public Product()
{
this.State = new ProductState();
}

public Product(string name, float price)
: this()
{
this.State.Name = name;
this.State.Price = price;
}

public ProductState State { get; private set; }

public Memento<ProductState> GetMemento()
{
return new Memento<ProductState>().Save(State);
}

public void SetMemento(Memento<ProductState> memento)
{
State = memento.Restore();
}

public override string ToString()
{
return String.Format(System.Globalization.CultureInfo.InvariantCulture,
"{0} {1:f2}", State.Name, State.Price);
}
}




Caretaker



Caretaker is the object that controls when to create Memento but the Originator will use the stored into the Caretaker state to restore from.


// Usage example and Caretaker as the same time.
static void Main(string[] args)
{
// Caretaker is in this example the class that holds the Main() void
// or the Main() void itself of you want.
Memento<ProductState> memento = new Memento<ProductState>();

Product product = new Product("Name one", 100f);
Console.WriteLine("Initial state: " + product.ToString());

memento = product.GetMemento();
Console.WriteLine("The state was saved to memento");

// Change the state of the Originator
product.State.Name = "Name two";
product.State.Price = 200f;
Console.WriteLine("State changed: " + product.ToString());

// Change the state of the Originator
product.State.Name = "Name three";
product.State.Price = 300f;
Console.WriteLine("State changed: " + product.ToString());

product.SetMemento(memento);
Console.WriteLine("The state restored from memento");

Console.WriteLine("Reverted state: " + product.ToString());

Console.ReadLine();
}




which gives after running the following output:




The Memento is save-state undo command, where the captured is a backup. In this example the Memento object used in-memory storage to keep backup of state, but the Memento object could also even persist state into a database.

Atanas Hristov

kick it on DotNetKicks.com
Shout it

1 comment:

  1. Thanks for the clear and succinct write up.

    rgds
    Sunit

    ReplyDelete