Introduction
The command design pattern is a behavioural design pattern in which a request can be turned to action.
Where to use a command design pattern?
When we need to
- Do/undo action
- When you want pass method as an argument or when you want to call a call back function
- When we want to log or track commands
Why use a command design pattern?
Command Design Pattern is a request-driven design pattern. A request will help choose which command needs to be executed without knowing which object.method to call. In this kind of case command, the design pattern plays a key role, making this easy by using ICommand. The command design pattern actually decouples command manager and command execution logic i.e. Actual implementation.
Players in this pattern
- ICommand – its interface which will do action undo action
- ConcreateCommand – the one who implements ICommand
- Invoker – One who carries out the action
- Receiver: One who receives action from invoker and performs action
Example
Problem definition - Provide an interface to the user to open and close the door. If the user wants, they should be able to undo his/her last action.
This example actually showcases the use case of the do and undo action requirement. For other requirements, I will write separate articles.
Players here
- ICommand – ICommand
- ConcreateCommand – DoorCommand
- Invoker – client, in our case we will client operation in main(home class) function
- Receiver – Door
We will create an enum for performing the actions:
- from enum import Enum
-
- class Action(Enum):
- DOOR_OPEN = 0,
- DOOR_CLOSE = 1
Below is the Command abstract class which will define the contact to implement:
- from abc import ABC, abstractmethod
- from actions import Action
-
-
- class Command(ABC):
-
- @abstractmethod
- def execute(self, action: Action):
- pass
-
- @abstractmethod
- def unDoExecute(self, action: Action):
- pass
Below is the DoorCommand class which implements the ICommand contract:
- from iCommand import Command
- from actions import Action
- from door import Door
-
- class DoorCommand(Command):
-
- def __init__(self):
- self.door = Door()
-
- def execute(self, action: Action):
- switcher = {Action.DOOR_OPEN: self.door.open, Action.DOOR_CLOSE: self.door.close}
- self.lastAction = action
-
- function = switcher.get(action, None)
-
- function()
-
- def unDoExecute(self):
- switcher = {Action.DOOR_OPEN: self.door.close, Action.DOOR_CLOSE: self.door.open}
-
- function = switcher.get(self.lastAction, None)
-
- function()
Below is the Door class, it actually has core logic implementation:
- class Door :
- def open(self):
- print("------------Door Opened---------------")
-
- def close(self) :
- print("------------Door Closed---------------")
Below is the Invoker, w hich requests an action
- from iCommand import Command
- from doorCommand import DoorCommand
- from actions import Action
-
- if __name__ == "__main__":
- command: Command = DoorCommand()
- command.execute(Action.DOOR_OPEN)
- command.execute(Action.DOOR_CLOSE)
- command.unDoExecute()
Below is the output window
Summary
In this article, we learned about the usage of a command design pattern, mainly concentrating on the do and undo operation.