Python Topics : Command Patterns
Understanding the Command Design Pattern
the Command design pattern helps transform a jumble of scattered function calls into well-organized commands which are easy to manage and extend
the Command design pattern encapsulates a request as an object allowing parameterized clients with
  • various requests
  • schedule or queue a request's execution
  • support undoable operations
pattern is especially valuable in applications that involve menus, queue operations, and transactional behavior
Advantages of the Command Pattern
consider developing a web application that allows users to perform actions such as
  • submitting forms
  • fetching data
  • processing files
the Command pattern is particularly suited for such scenarios
  • encapsulation - commands wrap all the details of an action — including the method call, its arguments, and the owning object—into a standalone object
  • decoupling - command separates the sender of a request from the receiver that executes it
    enhances both flexibility and component reusability
  • extensibility - new commands can be introduced without altering existing code
    adheres to the open/closed principle
Key Components of the Command Pattern
  • command interface - an abstract base that defines the standard interface for all commands
  • concrete command - the object implements the command interface and encapsulates all details required to perform an action
  • invoker - the object triggers the command to execute the request
    invokers can either be class or a function
  • receiver - knows how to carry out the operations encapsulated by the command
Implementing the Command Pattern
import os
from abc import ABC, abstractmethod
from typing import TextIO

class FileReceiver:
''' manages the file, defining methods for opening, writing to, and closing the file '''
    def __init__(self, filename: str):
        self.filename = filename
        self.file: TextIO | None = None

    def open_file(self):
        self.file = open(self.filename, 'w')
        print(f"File {self.filename} opened.")

    def write_file(self, text: str):
        if not self.file:
	         raise Exception("File is not open")
        self.file.write(text)
        print("Written to file:", text)


    def close_file(self):
        if self.file:
            self.file.close()
            print(f"File {self.filename} closed.")
        else:
            raise Exception("File is not open")

class Command(ABC):
    @abstractmethod
    def execute(self):
        pass

class OpenFileCommand(Command):
'''  encapsulate the operation on the file '''
    def __init__(self, file_receiver: FileReceiver):
        self.file_receiver = file_receiver

    def execute(self):
        self.file_receiver.open_file()

class WriteFileCommand(Command):
'''  encapsulate the operation on the file '''
    def __init__(self, file_receiver: FileReceiver, text: str):
        self.file_receiver = file_receiver
        self.text = text

    def execute(self):
        self.file_receiver.write_file(self.text)

class CloseFileCommand(Command):
'''  encapsulate the operation on the file '''
    def __init__(self, file_receiver: FileReceiver):
        self.file_receiver = file_receiver

    def execute(self):
        self.file_receiver.close_file()

class FileOperation:
''' invoker manages and executes the commands
    could be enhanced to support operations like undoing, queuing, or logging '''
    def __init__(self, commands: list[Command]):
        self.commands = commands

    def execute_commands(self):
        for command in self.commands:
            command.execute()

file_receiver = FileReceiver("example.txt")
commands = [
    OpenFileCommand(file_receiver),
    WriteFileCommand(file_receiver, "Hello, this is a test."),
    CloseFileCommand(file_receiver)
]

file_operations = FileOperation(commands)
file_operations.execute_commands()
index