Momento pattern
Introduction:
Momento is a behavioral design pattern. It helps to store the objects internal state, and the object can return to its previous state later without violating the encapsulation.
Example,
Text editor provides the option to the user to save the current state and go back to the previous state when user press undo button.
When to use,
Need to restore an object back to its previous state (eg undo or rollback).
How does it work,
There are three components. 1. Originator 2.Momento 3.Caretaker.
Originator:
- Originator is the class for which we need to save its internal state and restore it back to the previous state when the user does undo operation.
- Momento pattern delegates creating the state snapshot to the actual owner of the state ie originator class.
- Because some of the fields are private fields, only originator class has full access to save its private fields.
- originator class has functionality to take snapshot of its internal state and restore it back.
- But originator class doesn't contain the snapshots.
Momento:
- This pattern suggests storing of object state in the special object called Momento.
Caretaker:
- Caretaker role asks originator to take the snapshot and can ask the originator to restore its previous state.
- Caretaker contains the snapshots in the form of momento objects.
The client (caretaker class ) asks momento from the source object when it needs to checkpoint the source object state. The source object (Orginator class) initializes momento with its internal state.
The client is the caretakers of the momento, but only the source objects can store and restore the state from the momento. If the client subsequently wants to restore the source object state, it will handover the momento to source object for reinstatement.
How to use,
- Identify the roles of “caretaker” and “originator”.
- Create a Memento class and declare the originator a friend.
- Caretaker knows when to "check point" the originator.
- Originator creates a Memento and copies its state to that Memento.
- Caretaker holds on to (but cannot peek into) the Memento.
- Caretaker knows when to "roll back" the originator.
- Originator reinstates itself using the saved state in the Memento.
Implementation:
Sourcecode: https://github.com/krishnaKSA/design_patterns_cplusplus/blob/main/MomentoPattern/MomentoPattern.hpp
Class Diagram:
Momento Class:
//Momento class Momento { //Orignator declared as friend class friend class Orginator;
//state uint8_t state;
//only orginator can access getstate uint8_t getState() { return this->state; }
public: Momento(uint8_t state) { this->state = state; } };
Originator class:
//Class orginator class Orginator { private: //state uint8_t state;
public: Orginator() { state = 0; } void doSomething() { state += 1; }
//take snapshot of the state Momento* saveState() { printf("Orginator saved state %d \n",this->state); return new Momento(this->state); }
//restore to previous state void undo(Momento* momento) { uint8_t currentState = this->state; this->state = momento->getState(); printf("Orginator restore state current state %d , restoring state %d \n",currentState, this->state); }
};
Caretaker class:
//Caretaker class class Caretaker { private: //Momento list vector<Momento*> momentoList;
//orginator object Orginator* orginator;
public: Caretaker(Orginator* org) { this->orginator = org; } //save the state void backup() { //get the momento from originator and save it to vector momentoList.push_back(orginator->saveState()); } //Undo or rollback void restore() { if(momentoList.empty()) { cout<<"Caretaker:: momento list is empty "<<endl; } else { //send the momento to orginator orginator->undo(momentoList.back()); //remove the last state from vector momentoList.pop_back(); } } };
Client code:
void testClientCode() { Orginator* org = new Orginator(); Caretaker* caretaker = new Caretaker(org); org->doSomething(); caretaker->backup(); org->doSomething(); caretaker->backup(); org->doSomething(); caretaker->restore(); caretaker->restore(); caretaker->restore(); caretaker->backup(); }
//Momento
class Momento
{
//Orignator declared as friend class
friend class Orginator;
//state
uint8_t state;
//only orginator can access getstate
uint8_t getState()
{
return this->state;
}
public:
Momento(uint8_t state)
{
this->state = state;
}
};
Originator class:
//Class orginator
class Orginator
{
private:
//state
uint8_t state;
public:
Orginator()
{
state = 0;
}
void doSomething()
{
state += 1;
}
//take snapshot of the state
Momento* saveState()
{
printf("Orginator saved state %d \n",this->state);
return new Momento(this->state);
}
//restore to previous state
void undo(Momento* momento)
{
uint8_t currentState = this->state;
this->state = momento->getState();
printf("Orginator restore state current state %d , restoring state %d \n",currentState, this->state);
}
};
Caretaker class:
//Caretaker class
class Caretaker
{
private:
//Momento list
vector<Momento*> momentoList;
//orginator object
Orginator* orginator;
public:
Caretaker(Orginator* org)
{
this->orginator = org;
}
//save the state
void backup()
{
//get the momento from originator and save it to vector
momentoList.push_back(orginator->saveState());
}
//Undo or rollback
void restore()
{
if(momentoList.empty())
{
cout<<"Caretaker:: momento list is empty "<<endl;
}
else
{
//send the momento to orginator
orginator->undo(momentoList.back());
//remove the last state from vector
momentoList.pop_back();
}
}
};
Client code:
void testClientCode()
{
Orginator* org = new Orginator();
Caretaker* caretaker = new Caretaker(org);
org->doSomething();
caretaker->backup();
org->doSomething();
caretaker->backup();
org->doSomething();
caretaker->restore();
caretaker->restore();
caretaker->restore();
caretaker->backup();
}