Bird
0
0
LLDsystem_design~7 mins

Command pattern in LLD - System Design Guide

Choose your learning style9 modes available
Problem Statement
When a system tightly couples the object that sends a request to the object that performs it, it becomes hard to extend, queue, or undo operations. This rigidity leads to code that is difficult to maintain and evolve, especially when new commands or complex workflows are needed.
Solution
The Command pattern encapsulates a request as an object, separating the sender from the receiver. This allows commands to be parameterized, queued, logged, or undone independently of the sender, enabling flexible and extensible command management.
Architecture
Client
(Invoker)
Command
ConcreteCommand

This diagram shows the Client (Invoker) sending requests to Command objects, which encapsulate actions executed by the Receiver. The ConcreteCommand implements the Command interface and calls the Receiver's methods.

Trade-offs
✓ Pros
Decouples the sender and receiver, enabling flexible command management.
Supports undo/redo operations by storing command history.
Allows queuing, logging, and scheduling of commands easily.
Facilitates adding new commands without changing existing code.
✗ Cons
Increases the number of classes and objects, adding complexity.
May introduce overhead due to additional layers of abstraction.
Requires careful design to manage command lifecycle and state.
Use when you need to parameterize objects with operations, support undo/redo, queue or log requests, or decouple request senders from receivers, especially in GUI applications or transactional systems.
Avoid when the system has very simple operations with no need for extensibility, undo, or queuing, as the added abstraction may be unnecessary overhead.
Real World Examples
Amazon
Uses the Command pattern in their order processing system to encapsulate order actions, enabling retries, cancellations, and audit logging.
Microsoft
Implements the Command pattern in Office applications to support undo/redo functionality for user actions.
Uber
Applies the Command pattern to manage ride requests and cancellations, allowing flexible command scheduling and retries.
Code Example
The before code tightly couples the Switch to the Light's methods. The after code uses the Command pattern to encapsulate requests as objects, allowing the Switch to execute commands without knowing the details of the Light. This decouples sender and receiver and enables flexible command management.
LLD
### Before (without Command pattern):
class Light:
    def turn_on(self):
        print("Light is ON")
    def turn_off(self):
        print("Light is OFF")

class Switch:
    def __init__(self, light):
        self.light = light
    def on(self):
        self.light.turn_on()
    def off(self):
        self.light.turn_off()

light = Light()
switch = Switch(light)
switch.on()
switch.off()

### After (with Command pattern):
from abc import ABC, abstractmethod

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

class Light:
    def turn_on(self):
        print("Light is ON")
    def turn_off(self):
        print("Light is OFF")

class TurnOnCommand(Command):
    def __init__(self, light):
        self.light = light
    def execute(self):
        self.light.turn_on()

class TurnOffCommand(Command):
    def __init__(self, light):
        self.light = light
    def execute(self):
        self.light.turn_off()

class Switch:
    def __init__(self, on_command, off_command):
        self.on_command = on_command
        self.off_command = off_command
    def on(self):
        self.on_command.execute()
    def off(self):
        self.off_command.execute()

light = Light()
on_command = TurnOnCommand(light)
off_command = TurnOffCommand(light)
switch = Switch(on_command, off_command)
switch.on()
switch.off()
OutputSuccess
Alternatives
Strategy pattern
Encapsulates interchangeable algorithms but does not encapsulate requests as objects for queuing or undo.
Use when: Choose when you need to select algorithms dynamically but do not require command queuing or undo functionality.
Mediator pattern
Centralizes communication between objects rather than encapsulating requests as objects.
Use when: Choose when you want to reduce direct dependencies between objects but do not need command encapsulation.
Summary
The Command pattern encapsulates requests as objects to decouple senders from receivers.
It enables flexible command management such as queuing, undo, and logging.
This pattern improves extensibility but adds abstraction and complexity.