Thursday, July 23, 2009

Generic implementation of memento pattern in C#

The memento pattern is a software design pattern that provides the ability to restore an object to its previous state (undo via rollback).

The memento pattern is used by two objects: the originator and a caretaker. The originator is some object that has an internal state. The caretaker is going to do something to the originator, but wants to be able to undo the change. The caretaker first asks the originator for a memento object. Then it does whatever operation (or sequence of operations) it was going to do. To roll back to the state before the operations, it returns the memento object to the originator.

In this post, I will show how to create a Generic implementation of the memento pattern that can be used to store and retrieve objects that can be serialized.

Originator

public class Originator

{

public T CurrentState { get; set; }

public T Store()

{

return Serialize(CurrentState);

}

public T Retrieve(T storedInstance)

{

CurrentState = storedInstance;

return CurrentState;

}

private Func Serialize = x =>

{

MemoryStream memoryStream = new MemoryStream();

BinaryFormatter binaryFormatter = new BinaryFormatter();

binaryFormatter.Serialize(memoryStream, x);

memoryStream.Seek(0, SeekOrigin.Begin);

T objectCopy = (T)binaryFormatter.Deserialize(memoryStream);

memoryStream.Close();

return objectCopy;

};

}

Caretaker

public class Caretaker

{

public T StoredInstance { get; private set; }

public Caretaker(T instance)

{

StoredInstance = instance;

}

}

The test class

[TestClass()]

public class When_Object_Has_State : ArrangeAssertActTearDown<Originator<User>>

{

User ___UserUnderTest;

Caretaker<User> ___UserCareTaker;

Guid ___OriginalGuid;

Guid ___IdToChange;

public override void Arrange()

{

___UserUnderTest = new User("Prajeesh", "Prathap", 1);

___OriginalGuid = ___UserUnderTest.Guid;

___IdToChange = new Guid("{900F1521-D3BD-4f60-A5F9-8548A098B61B}");

}

public override void Act()

{

SystemUnderTest.CurrentState = ___UserUnderTest;

}

[TestMethod]

public void Must_Restore_To_Old_State_On_Undo()

{

___UserCareTaker = new Caretaker<User>(SystemUnderTest.Store());

User __User = SystemUnderTest.CurrentState;

__User.Guid = ___IdToChange;

Assert.AreEqual<Guid>(___IdToChange, __User.Guid);

__User = SystemUnderTest.Retrieve(___UserCareTaker.StoredInstance);

Assert.AreEqual<Guid>(___OriginalGuid, __User.Guid);

}

public override void TearDown()

{

___UserUnderTest = default(User);

___UserCareTaker = default(Caretaker<User>);

}

}

No comments: