Command pattern is a behavioural design pattern in which the object is encapsulate all the information to be executed at later time.
To understand this pattern let’s take an example of signing up for an account.
So let’s assume that registration is three step procedure. First step is to fill basic details, second step to fill business details and then final step to fill payment details. Initially object of Command is created and will be used to collect all information. Once all information of these three steps are filled you would hit Register button and Command object which has encapsulated all the information in it and execute method will be executed to register the account.
Useful terms
- Command - Executes receiver’s action on invoking by Invoker
- Client: Create the Invoker object and also sets the receiver object
- Invoker: This actually manage the commands by specific invoke method.
- Receiver: Ultimate action performer.
Understanding of command pattern —
- Object of
Command
has knowledge ofReceiver
so that can call method ofReceiver
object. All the values will be stored in object ofCommand
class and those values will be passed toReceiver
object method. Invoker
has details to execute command.Client
object passes aReceiver
object while creatingCommand
object.
How to implement —
'''
Command Design Pattern (Behavioral Design Pattern)
'''
from abc import ABC, abstractmethod
class BaseCommannd(ABC):
'''
Base command class
'''
@abstractmethod
def execute(self):
raise NotImplementedError("Please implement in subclass")
class EMailCommand(BaseCommannd):
'''
Email Command class
'''
def __init__(self, receiver, data):
self.receiver = receiver
self.data = data
def execute(self):
self.receiver.send_email(self.data)
class SMSCommand(object):
'''
Command class
'''
def __init__(self, receiver, data):
self.receiver = receiver
self.data = data
def execute(self):
self.receiver.send_sms(self.data)
class NotificationService(object):
'''
Receiver class
'''
def send_email(self, data):
print("Sending email", data)
def send_sms(self, data):
print("Sending short message", data)
class NotificationInvoker(object):
'''
Invoker class
'''
def __init__(self):
self.notification_history = []
def invoke(self, command):
self.notification_history.append(command)
command.execute()
if __name__ == "__main__":
invoker = NotificationInvoker()
sender = NotificationService()
invoker.invoke(EMailCommand(sender, {"subject": "Test Email"}))
invoker.invoke(SMSCommand(sender, {"subject": "Test SMS"}))
print(invoker.notification_history)
In above example,
NotificationService
(Receiver
) is ultimate executer of the action.
BaseCommand
is the command interface. We have two concrete command class — SMSCommand
and EMailCommand
NotificationInvoker
is invoker class which actually call the execute method of the command object. Here all the commands are stored in notification_history
object. Which cab be used to re-execute all the commands as those command object contains all the information to execute again.
Here, Client
class is not implemented as we are the client using all above classes as all steps are mentioned in end of the code.
First, we create objects of Receiver
and Invoker
class. We also create objects of EMailCommand
or SMSCommand
class with passing arguments and pass it to invoke method of Invoker
class object. When you invoke any command Invoker
will call execute method of command object and command will call the method of Receiver
object with all the information are stored in Command
object.
In above example, We are executing methods on instant basis. But we can modify Invoker
where we can set scheduling information that way we can make it asynchronous. We can also modify Command
classes where we can provide facility to consolidate all the information in different way.
This pattern can be used in our projects with few tweaks according to your needs.
Benefits of this pattern —
- Re-perform or Rollback operation can be performed.
- Queue system can be built to execute commands in sequence.
- New type of command can be introduced with very few changes.