In my last article I spoke about the Mediator pattern; today we will have a glance at the Memento Patten.
Agenda
- What is the Memento Pattern?
- When to use the Memento Pattern?
- Journey to the Memento Pattern.
- What are the components of the Memento Pattern?
- How to implement the pattern using C#? There is a code walkthrough here.
- Class Diagram.
Previous Articles
- Design Patterns - Introduction
- Learn Design Pattern - Singleton Pattern
- Learn Design Pattern - Factory Method Pattern
- Learn Design Pattern - Abstract Factory Pattern
- Learn Design Pattern - Builder Pattern
- Learn Design Pattern - Prototype Pattern
- Learn Design Pattern - Adapter Pattern
- Learn Design Pattern - Composite Pattern
- Learn Design Pattern - Decorator Pattern
- Learn Design Pattern - Facade Pattern
- Learn Design Pattern - Flyweight Pattern
- Learn Design Pattern - Bridge Pattern
- Learn Design Pattern - Proxy Pattern
- Learn Design Pattern - Mediator Pattern
(For training related to .Net, OOP and design patterns contact me at [email protected].)
What is Memento Pattern?
The GOF says the "Memento Pattern lets us capture and externalize an object's internal state so that the object can be restored to this state later".
In short it adds the ability to UNDO to objects.
When to use Memento Pattern?
Can anyone tell me which is the best feature provided in Visual Studio?
Any guess? It's Undo and Redo. I don't think there is any developer who hasn't used this and praised Microsoft for the feature.
In OOP, we create an object of a class, assign values to properties (in other words we change the state of an object). Later we again change the state.
Now the point is: What if, it requires to rollback an object to a previous state?
Journey to Memento Pattern
Main Problem
Some provision for storing and retrieving the state of an object is needed.
Solution 1
Create 2 instances of the same object. One will be used as the backup object.
Problem 1
Solution 1 not accepted.
What if only part of the object needs to be restored? For example we have a Car object with 2 properties CurrentSpeed and TyreLife and it's possible to restore the Current speed but not Tyrelife.
Solution 2
Create one more class which will act as a Memento object that will contain a replica of the properties in the original class which can be restored.
For above example we will create a class called CarMemento with a single property called CurrentSpeed.
Problem 2
Solution 2
Seems fine and can be accepted but, where will be the backup (code that copies the value from an actual object to a Memento object) and restore logic (code that copies value from a Memento object to an actual object) will be?
Writing such logic in the final client code, makes the client code more complicated plus it's not possible to reuse the same backup and restore logic for other clients.
Solution 3
Backup and restore logic will be a part of the original class.
Here the Car class will have two member functions, SaveBackup and RestoreBackup.
These 2 functions answer WHAT to be stored and restored.
Problem 3
Solution 3 accepted.
We understad now. Now the original class will be responsible for its backup and restoration. It will create the Memento object which will be used by itself later for restoration purposes.
Wait a minute!! Where will the Memento object created by the original class (Car class) will be stored?
Solution 4
A separate class is created called Caretaker, which will store the Memento object created by the Original class.
Considering the example we are discussing about Car, the solution will be to create a class called CarCareTaker with the property of type CarMemento.
Question
Why is a separate class required for storing the Memeto Object, why we can't use the original class (Car class) for this purpose? (In other words why don't we create a property of type CarMemento in the Car class itself?)
Answer
Consider the client wants to store multiple backups, so that multiple undo is possible. Or it may possible, one asks for the restoration by a Time Period (restore my object to a state it was at 20/10/2012 2 PM).
Now the logic which decides how restoration and backup is to be done becomes more complex and it violates the SRP – Single Responsibility Principle.
Solution 4 accepted
Different components of Memento Pattern
Finally we conclude with the following components:
1. Originator – Class whose state needs to be restored. It will define methods that will specify what needs to be restored.
2. Memento – Class which contains replica of states defined in the Originator class. The Originator will be responsible for its creation.
3. CareTaker – Decides how restoration works.
Note: CareTaker is not required to always exist. Some people just skip that.
It depends on your problem statement, if you want to keep your "How backup and restore work" logic separate from "What is need to be backup and restore" logic, then probably use this class.
(For training related to .Net, OOP and design patterns contact me at [email protected])
Practical Demonstration
Output
Code Walkthrough:
Step 1
Create Concrete Class Car
public class Car
{
public int CurrentSpeed{get;set;}
public int TyreLife{ get;set;}
}
Step 2
Create Memento Class
internal class CarMomento
{
public int CurrentSpeed{get;set;}
}
Step 3
Add SaveBackup and RestoreBackup methods to the Car Class to decide what needs backup and restore.
internal CarMomento SaveBackup()
{
return new CarMomento()
{
CurrentSpeed = this.CurrentSpeed
};
}
internal void RestoreBackup(CarMomento objTaker)
{
this.CurrentSpeed = objTaker.CurrentSpeed;
}
Step 4
Create Care Taker
public class CarCareTaker
{
List<CarMomento> CarBackups = new List<CarMomento>();
public void CreateBackup(Car objCustomer)
{
CarBackups.Add(objCustomer.SaveBackup());
}
public void RestoreBackup(Car objCustomer,int Index)
{
objCustomer.RestoreBackup(CarBackups[Index]);
for(int i = CarBackups.Count - 1; i >=Index; i--)
{
CarBackups.RemoveAt(i);
}
}
}
Step 5
Client Code
Car objCar;
CarCareTaker objCareTaker = new CarCareTaker();
void btnBackup_Click(object sender, EventArgs e)
{
Button objSender = sender as Button;
objCareTaker.RestoreBackup(objCar, int.Parse(objSender.Tag.ToString()) - 1);
this.FunPriSetControls();
}
private void BtnIncreaseSpeed_Click(object sender, EventArgs e)
{
objCareTaker.CreateBackup(objCar);
objCar.CurrentSpeed++;
objCar.TyreLife -= 10;
this.FunPriSetControls();
}
Class Diagram
Hope you enjoyed reading this article.
Comments and feedbacks are always welcome.
(For training related to .Net, OOP and design patterns contact me at [email protected])