The Command Design Pattern allows us to encapsulate and record the action that can be performed on an entity in a system. The key motivation behind the command design pattern is to decouple the two communicating bodies in a system.
Command Design Pattern suggests creating an interface called “Command”, which will encapsulate and store the last action performed in an entity.
Let’s say there are two entities E1 and E2, here an entity simply means an object of an interface. E1 wants to perform an action or make a change to E2. So, firstly E1 will contact the “Command interface” then Command will perform the action on E2 and also record the action performed.
One can think of why we need to keep track of actions or changes performed because What if we want to undo the action? Command Pattern helps to revert back to the previous state and decouples the entities.
Memento Pattern also works the same that is Memento Pattern also facilitates the storage of the actions performed, but with Command Pattern only the last action will be stored whereas in Memento Pattern you can store each and every action performed.
Components of Command Design Pattern
- Command: Command class consist of method execute() for executing an operation.
- ConcreteCommand: Child class of “Command” extends the Command interface, implementing the execute() method by invoking the corresponding operations on Receiver. It defines a link between the Receiver and the action.
- Invoker: Invoker class which decides when to invoke execute() and asks the Command to carry out the request.
- Undo: Undo will the member method of Invoker that will lead to revert back.
Example
Consider a simple example of addition and subtraction in a variable.
- Class
data
, have class variable_data
and abstract methods for addition & subtraction into class variable. Also has__str__
magic metod to return present value of class variable.
class data:
_data = 0
def __init__(self, value=0) -> None:
data._data += value
@abstractmethod
def add(self):
pass
@abstractmethod
def subtract(self):
pass
def __str__(self) -> str:
return f"Present value: {data._data}"
Addition
andSubtraction
children ofdata
parent class, having implementation ofadd()
andsubtract()
method respectively.
class Addition(data):
def __init__(self, value=0) -> None:
super().__init__(value)
def add(self, value):
data._data += value
print("Addition is being performed...")
def __str__(self) -> str:
return super().__str__()
class Subtraction(data):
def __init__(self, value=0) -> None:
super().__init__(value)
def subtract(self, value):
data._data -= value
print("Subtraction is being performed...")
def __str__(self) -> str:
return super().__str__()
- The abstract command class having declaration for
execute()
andundo()
method.
class Command():
@abstractmethod
def execute(self):
pass
@abstractmethod
def undo(self):
pass
- Children classes (or Concrete classes) of abstract command class —
AdditionCommand
andSubtractionCommand
, having constructor to store the calling object and the value to be added or subtracted intodata
class variable, implementation ofexecute()
method andundo()
method.
class AdditionCommand(Command):
def __init__(self, obj, value):
self.obj = obj
self.value = value
def execute(self):
self.obj.add(self.value)
def undo(self, commandObj):
commandObj.set_command(SubtractionCommand(Subtraction(), self.value))
commandObj.invoke()
class SubtractionCommand(Command):
def __init__(self, obj, value):
self.obj = obj
self.value = value
def execute(self):
self.obj.subtract(self.value)
def undo(self, commandObj):
commandObj.set_command(AdditionCommand(Addition(), self.value))
commandObj.invoke()
- The invoker class —
ActionInvoker
, having constuctor to store the command to invoke,set_command()
method to change the stored command,invoke()
method to callexecute()
method of ConcreteCommand classes, and undo method to callundo()
method of ConcreteCommand classes.
class ActionInvoker:
def __init__(self, command):
self.command = command
def set_command(self, command):
self.command = command
def invoke(self):
self.command.execute()
def undo(self):
print("[!UNDO] ", end="")
self.command.undo(self)
- Finally, the driver code.
if __name__ == '__main__':
obj = data(100)
print(obj)
command_addition = AdditionCommand(Addition(), 10) # concrete command
command_subtraction = SubtractionCommand(Subtraction(), 10) # concrete command
action_invoker = ActionInvoker(command_addition); # invoker
action_invoker.invoke()
print(obj)
action_invoker.set_command(command_subtraction)
action_invoker.invoke()
print(obj)
action_invoker.undo()
print(obj)
action_invoker.undo()
print(obj)
Output
Present value: 100
Addition is being performed...
Present value: 110
Subtraction is being performed...
Present value: 100
[!UNDO] Addition is being performed...
Present value: 110
[!UNDO] Subtraction is being performed...
Present value: 100
Command Pattern enables to implementation of the functionalities of UNDO/REDO. Helps in encapsulating all the information needed to perform an action or an event.
The complexity of the code increases as we are introducing certain layers between the senders and the receivers(i.e communicating entities). Every individual command is a ConcreteCommand class that increases the volume of the classes for implementation and maintenance.