Command Pattern: Request Encapsulation and Undo

The Command Pattern is a behavioral pattern that encapsulates requests as objects, thereby decoupling the sender of a request from its receiver. It also allows for queuing or delaying command execution. In this pattern, a request is encapsulated into a command object containing all necessary information to execute the command, including references to the receiver and details about how to execute it.

The command object holds an implementation of the method that executes the action and maintains a reference to its receiver, enabling the sending of requests for execution by the receiver. Additionally, these objects can include extra data, such as parameters, making commands more flexible and extensible.

Implementation Roles

Typical implementations of the Command Pattern involve several key roles:

  1. Abstract Command Class (Command): Defines the interface for command objects, including methods to execute a command and undo it.
  2. Concrete Command Classes (Concrete Command): Implements the interfaces defined by abstract commands, providing specific execution logic and references to receivers.
  3. Receiver Class: Contains the actual action that needs to be performed.
  4. Invoker Class: Creates and sends command objects to their respective receivers for execution.
  5. Client: Responsible for creating and configuring command objects before sending them to the invoker.

Core Concept and Applicability

The primary idea behind the Command Pattern is encapsulating requests as objects, which decouples senders from receivers while allowing delayed or queued execution. This pattern is particularly suitable when:

  1. Decoupling the sender of a request from its receiver.
  2. Delaying or queuing command execution.

Benefits and Trade-offs

The Command Pattern enhances system flexibility and extensibility by decoupling senders from receivers and enabling queued execution. Furthermore, it supports undo (and potentially redo) functionality because commands can include an undo method. However, this pattern may increase code complexity due to the need for defining additional command classes to encapsulate specific requests.

class Command:
    """Abstract interface for a command."""
    
    def execute(self):
        pass
    
    def undo(self):
        pass


class ConcreteCommand(Command):
    """Concrete implementation of a command."""
    
    def __init__(self, receiver):
        self.receiver = receiver  # Reference to the Receiver

    def execute(self):
        print(f"Executing action on {type(self.receiver).__name__}")

    def undo(self):
        print("Undoing previous execution")


class Invoker:
    """Class responsible for executing commands."""
    
    def __init__(self, command_obj):
        self.command = command_obj  # Reference to the Command object
    
    def set_command(self, cmd):
        self.command = cmd

    def run(self):
        if self.command is not None:
            print("Running...")
            self.command.execute()


class Client:
    """Client class that creates and configures commands."""
    
    def __init__(self):
        pass
    
    def create_command_and_run(self, receiver_type="Receiver"):
        # Create a concrete command instance
        cmd = ConcreteCommand(receiver=receiver_type)
        
        # Assign the command to the invoker
        invoker = Invoker(cmd)
        
        # Execute the command via the invoker
        print("Client: Starting...")
        invoker.run()


# Example usage simulation
if __name__ == "__main__":
    client = Client()
    client.create_command_and_run(receiver_type="MyReceiver")