×

Command Design Pattern with Python

Command Design Patterns Python Feature

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 and Subtraction children of data parent class, having implementation of add() and subtract() 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() and undo() method.
class Command():
    @abstractmethod
    def execute(self):
        pass

    @abstractmethod
    def undo(self):
        pass
  • Children classes (or Concrete classes) of abstract command class — AdditionCommand and SubtractionCommand, having constructor to store the calling object and the value to be added or subtracted into data class variable, implementation of execute() method and undo() 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 call execute() method of ConcreteCommand classes, and undo method to call undo() 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.