The memento pattern is a Behavioural Design Pattern that provides the ability to restore an object to its previous state.
Consider a bank account, here anybody can deposit or withdraw the money in an account, that is bank account as an entity goes through changes, depending upon the amount of money deposited or withdrawn from the bank account.
Memento Pattern facilitates navigating through each and every changes an entity goes through. Similar to Command Pattern, in Command pattern, we create an interface to undo the changes, whereas, in Memento Pattern, we simply save snapshots of the system.
The Memento pattern is about capturing and storing the current state of an object in a manner that can be restored to the previous state later on in a smooth manner.
The Memento pattern suggests creating an interface whose object is used to store the current state of various entities, in such a way that the saved state data of the entities is not accessible outside of the memento interface object.
Components of Memento Pattern
The Memento pattern uses three actor classes.
- Memento: Memento class contains the state of an object to be restored.
class Memento:
def __init__(self, value):
self.state = value
def SetState(self, value):
self.state = value
def GetState(self):
return self.state
- Originator: Originator class creates and stores states in Memento objects.
class Originator:
def SetState(self, value):
self.state = value
def GetState(self):
return self.state
def CreateMemento(self):
return Memento(self.state)
def SetMemento(self, memento):
print("Going to previous state.")
self.state = memento.GetState()
- Caretaker: Caretaker class’s object is responsible to restore object state from Memento.
Note: Not necessarily there will be a separate class for Caretaker, Originator class itself can serve as caretaker.
class Caretaker:
def __init__(self, originatorObj):
self.memento = None
self.origin = originatorObj
def Execute(self):
self.memento = self.origin.CreateMemento()
self.origin.SetState(0)
def Unexecute(self):
self.origin.SetMemento(self.memento)
Driver code for the above implementation of Memento, Originator and Caretaker would be like this:
if __name__ == "__main__":
originator = Originator()
originator.SetState(1)
print("The state value is: ", originator.GetState())
caretaker = Caretaker(originator)
caretaker.Execute()
print("The state value is: ", originator.GetState())
caretaker.Unexecute()
print("The state value is: ", originator.GetState())
Output
The state value is: 1
The state value is: 0
Going to previous state.
The state value is: 1
Example
One more example of how an abstract Memento pattern can be implemented using Python:
- Memento class
class Memento(object):
def __init__(self, state):
self._state = state
def get_saved_state(self):
return self._state
- Originator class, serving as caretaker-class as well.
class Originator(object):
_state = ""
def set(self, state):
self._state = state
print("Originator: Setting state to", self._state)
def save_to_memento(self):
print("Originator: Saving to Memento.")
return Memento(self._state)
def restore_from_memento(self, memento):
self._state = memento.get_saved_state()
print("Originator: State after restoring from Memento:", self._state)
- The driver code
Using a List data structure to store all the changes that an entity goes through.
saved_states = []
originator = Originator()
originator.set("State-1")
originator.set("State-2")
saved_states.append(originator.save_to_memento())
originator.set("State-3")
saved_states.append(originator.save_to_memento())
originator.set("State-4")
originator.restore_from_memento(saved_states[0])
Output
Originator: Setting state to State-1
Originator: Setting state to State-2
Originator: Saving to Memento.
Originator: Setting state to State-3
Originator: Saving to Memento.
Originator: Setting state to State-4
Originator: State after restoring from Memento: State-2
Pros and Cons of Memento Design Pattern
Pros
- Doesn’t violate the encapsulation of the input object.
- Simplifies the structure of the input object. It doesn’t need to keep a history of its state versions.
Cons
- Requires a lot of memory if clients changes its state too often.
- Memento interface can entail additional memory costs, if the objects storing the history don’t release the resources occupied by the outdated states stored.