Command design pattern in python

Definition: A Command Pattern is responsible for encapsulating a request under an object as a command, which is then passed to the invoker object. The invoker object is responsible for searching for the appropriate handler of the request and passing the Command to the corresponding object, and that object executes the Command. It is also known as action or transaction.

It is one of the variations of behavioral design patterns, and here an interface will be created, which will act as a command. The corresponding request will be converted to one single object containing all the information. Then this object will be passed as an argument then some methods will be used to perform the necessary execution.

Important terminologies :

  • Command: This is an interface that is responsible for executing the requests.
  • Client: This creates a concrete class, and it is attached to the receiver's end.
  • Concrete Command: Binding between action and receiver is important, which is done by concrete Command. The interface is extended by it.
  • Invoker: Command is subjected to execute the request by the invoker. The invoker object is responsible for searching for the appropriate handler of the request and passing the Command to the corresponding object, and that object executes the Command.
  • Receiver: The necessary actions and tasks are performed using the receiver.

Problem statement :

Suppose you are trying to make something similar to a pdf editor. You made a prototype and started building the website/ application. We all know that any editor's toolbar is the most important part. They are the one that executes all the tasks. So for the toolbar, you need to create good buttons. Now you have created a buttons class for the toolbar. Each button will be created under this class. But there is a problem, the toolbar will contain many buttons, and each button will have different functionality, so how would we add code for all different functionalities. This problem can be solved using a conventional method where we will create many subclasses, and the required functionalities are implemented. These subclasses will contain code for each functionality. This solution is not practical because, firstly, you are creating such numbers of subclasses, and secondly, whenever there need to change something in the base class, we have to break the whole structure into another subclass. The code becomes bulky, complex, and even ugly, so we need to have a proper solution to this problem to boost the performance of our software. How can we do that?

Solution statement :

The best way to do this is to break the existing software into layers. For example, here, we will break the software into two layers: the first GUI layers and the second for business logic. The first layer will be responsible for creating a beautiful GUI and providing a reliable user experience because how the code works is not a concern or a matter of interest to the user. The user is only interested in the speed and efficiency of the applications. Then we will create a command interface that will help us in the execution of operations, and it will link to the business layer. We have to declare Command for every operation, and then we can implement our buttons for the application without making the code complex or bulky.

A real-life example of a command design pattern :

Suppose you decided to have a family dinner, so you and your family member visited a nearby restaurant, then you decided on the cuisine and the waiter came to take the order, you told the waiter what you want. The waiter wrote everything on paper, then went to the chef and gave him that paper. The chef will prepare the meal and put it on the table with the order. Then the waiter comes and takes that plate and delivers it to your table. So here, the paper(where the waiter had written all you order) is acting as a command. All you did was say what you wanted, and you got everything on your table. The command design pattern follows the same terminology. It will take the request and be executed in a step-by-step fashion, and you will get your desired result.

Example :

Let's use the command method in the simplest way for better understanding. Suppose we are building a room automation system where we will be adding the feature of opening and closing the door to create an interface for opening and closing the door. Then we will use the Command to perform the functionality. Let's try to implement this using code.

Example.py

from abc import ABC, abstract method
class _Command(ABC):
    """
   this interface will help to implement the commands easily
    """
    @abstractmethod
    def execute(self) -> None:
        pass
class room_open_Command(_Command):
  """
  Implements interface Command and provide open garage door functionality.
  """
  def __init__(self,room_receiver):
    self._room_receiver =room_receiver
  def execute(self) -> None:
    print("Command to open room door.")
    self._room_receiver.open_door()
class room_Close_Command(_Command):
  """
  Implements interface for opening and closing the room door functionality.
  """


  def __init__(self, room_receiver):
    self._room_receiver = room_receiver
  def execute(self) -> None:
        print("Command to close room door")
    self._room_receiver.close_door()
class room_Receiver:
  """
the   room door endpoints  open and close the door functionality is implemented
  """
  def open_door(self):
    print("Opening room door.")
  def close_door(self):
    print("Closing room door")
class ApplicationInvoker:
  """
  APIs to provide   Application code for opening and closing of the door
  """
  def __init__(self):
    receiver = room_Receiver()
    self._open_door = room_open_Command(room_receiver=receiver)
    self._close_door = room_Close_Command(room_receiver=receiver)
  def invoke_open_door(self):
    print("App initiated room door open.")
    self._open_door.execute()
  def invoke_close_door(self):
    print("App initiated room door close.")
    self._close_door.execute()
if __name__ == '__main__':
  # create an object for opening the door with class ApplicationInvoker
  app = ApplicationInvoker()
  # lets the invoker for opening call open door first
  app.invoke_open_door()
  print()
  # lets the invoker for opening call close the door
  app.invoke_close_door()

Output.txt :

App initiated room door open.
Command to open room door.
Opening room door.


App initiated room door close.
Command to close the room door
Closing room door

Explanation :

Firstly we have created an interface for handling all the commands, and there are two commands one for opening the door and the second for closing the door. And then, we have created a receiver for executing the Command, and our Command gets executed.

Interface :

class _Command(ABC):
    """
   this interface will help to implement the commands easily
    """


    @abstractmethod
    def execute(self) -> None:
        pass

This is our main interface which uses the abstract method for executing the process. It helps in the handling of the commands.

Door open command :

class room_open_Command(_Command):
  """
  Implements interface Command and provide open garage door functionality.
  """
 def __init__(self,room_receiver):
    self._room_receiver =room_receiver
  def execute(self) -> None:
    print("Command to open room door.")
    self._room_receiver.open_door()

This Command is responsible for opening the door. When called, it will print the message - Command to open the room door. If all the passed data is correct, it will call the recipient and execute the program. Here you can see we have the user receiver as an argument, so it is clear that the receiver will execute the Command, and this class is only for taking requests from the user.

The door closed. Command:

class room_Close_Command(_Command):
  """
  Implements interface for opening and closing the room door functionality.
  """


  def __init__(self, room_receiver):
    self._room_receiver = room_receiver


  def execute(self) -> None:
    
    print("Command to close room door")
    self._room_receiver.close_door()

This Command is responsible for closing the door. When called, it will print the message - Command to close the room door. If all the data is correct, it will call the recipient, and the program is executed. Here you can see we have a user receiver as an argument, so it is clear that the receiver will execute the Command, and this class is only for taking requests from the user.

Receiver  :

class room_Receiver:
  def open_door(self):
    print("Opening room door.")
 def close_door(self):
   print("Closing room door")

The receiver is calling the individual methods to implement all the necessary tasks. The invoker will call the API, and then we will get our desired result.

Giving Command:

app.invoke_open_door()
  print()

This is the method for calling the function or initiating the Command. The code above represents the basic syntax for the process.

So this is how the code is working and implementing the desired solution.

Advantages of command design pattern :

  • The class is decoupled, which helps in invoking the operation from the desired object, and then the program is executed.
  • We use a queue system for Command to boost the software's performance.
  • We can add new Commands very easily, and it does not affect the basic structure of the code. Adding a new subclass through the conventional method is poor practice, making software less efficient, so using command design patterns is helpful and efficient.
  • It can consist of the functionality of the roll-back system.

Disadvantages of command design pattern :

  • The developer needs to be careful while creating the classes, as there are several classes.
  • Each class works as a concrete class which is the major drawback.

This was all about command design patterns, I hope you got the basic knowledge of command design patterns.



ADVERTISEMENT
ADVERTISEMENT