Command Pattern: Explore Command-Query Separation

The Command pattern is a behavioral design pattern that encapsulates a request as an object, allowing the parameterization and queuing of operations. It decouples the sender of a request from the receiver, providing flexibility and extensibility in handling requests.

The Command design pattern allows for various operations to be represented as different command objects, enabling flexibility in adding new commands or modifying existing ones without impacting the sender.

The Command design pattern promotes loose coupling between components, enhances code flexibility, and facilitates the implementation of complex command-based systems.

Real World Scenario

Imagine you’re in a restaurant. When you’re ready to order, a waiter comes to your table and writes down your order on a piece of paper. The waiter takes this paper and sticks it on the kitchen wall. The chef sees the order, reads it, and starts cooking the meal based on the information written on the paper. Once the meal is ready, the chef places it on a tray together with the order. The waiter notices the tray, checks the order to ensure everything is correct, and brings both the meal and the order to your table.

In this situation, the piece of paper with the order written on it acts as a command. It stays in a queue on the kitchen wall until the chef is ready to prepare it. The order contains all the necessary details for the chef to start cooking without needing to clarify the order directly with you.

By using the Command design pattern in this context, the process becomes more organized and efficient. The waiter acts as an invoker, the chef as the receiver, and the paper order serves as the command that carries the information needed for the chef to prepare the meal accurately.

Applicability

The Command design pattern is applicable in the following scenarios:

  1. Need for decoupling: When you want to decouple the sender of a request from the receiver, allowing them to vary independently. Also read – command-query segregation
  2. Undo/Redo functionality: When you want to implement undo and redo operations in an application by storing and executing commands.
  3. Queueing and scheduling requests: When you need to queue and schedule requests, enabling features like job processing or task management.
  4. Logging and auditing: When you want to log or audit operations for debugging, monitoring, or tracking purposes.
  5. Transactional behavior: When you want to enforce transactional behavior by treating a group of commands as a single unit of work.

Known Usages

The Command design pattern is commonly used in various software development scenarios, including:

  1. GUI frameworks: Command objects are frequently used to handle user interactions in graphical user interface frameworks, enabling actions to be triggered by buttons, menu items, or keyboard shortcuts.
  2. Undo/Redo functionality: Command pattern is widely used to implement undo/redo functionality in applications, allowing users to revert and reapply operations.
  3. Job scheduling systems: Command objects can be used to represent and schedule jobs or tasks, allowing them to be queued, executed, and managed efficiently.
  4. Remote control systems: Command pattern is often employed in remote control systems to encapsulate and transmit commands to remote devices, enabling control over various functionalities.
  5. Transactional systems: Command objects are utilized to encapsulate database operations within a transaction, ensuring that a group of commands is executed as a single atomic unit.
  6. Event-driven architectures: Command pattern is used to handle events and trigger corresponding actions in event-driven architectures, allowing loose coupling between event producers and event consumers.
  7. Macro recording/playback: Command objects can be recorded and played back to automate a sequence of actions, commonly used in applications like macro recorders or test automation tools.
  8. Plugin systems: Command pattern is often utilized in plugin architectures to allow extensibility, where plugins can register and execute commands dynamically.
  9. Workflow engines: Command objects can represent individual steps or tasks in a workflow, allowing the orchestration and execution of complex business processes.
  10. Virtual assistants: Command pattern is employed in virtual assistant applications to encapsulate and execute voice commands, enabling control over various functions and services.

Here’s are some examples of command pattern running in production of many systems:

Implementation

Components involved in Command Pattern

The Command design pattern involves several components:

  • Command: Interface/Abstract class that declares the execution method.
  • Concrete Command: Implements the Command interface and encapsulates a specific action.
  • Receiver: Performs the actual work associated with the command.
  • Invoker: Triggers the execution of the command by calling the execute() method.
  • Client: Creates command objects, associates them with receivers, and configures the invoker with the appropriate commands.
command pattern - neatcode
// Command
interface OrderCommand {
    void execute();
}
// Concrete Command
class CookOrderCommand implements OrderCommand {
    private Chef chef;
    public CookOrderCommand(Chef chef) {
        this.chef = chef;
    }
    public void execute() {
        System.out.println("CookOrderCommand: Execute command.");
        chef.prepareMeal();
    }
}
// Receiver
class Chef {
    public void prepareMeal() {
        System.out.println("Chef: Meal prepared.");
    }
}
// Invoker
class Waiter {
    private OrderCommand command;
    public void setCommand(OrderCommand command) {
        this.command = command;
    }
    public void submitOrder() {
        System.out.println("Waiter: Submit order.");
        command.execute();
    }
}
// Client
public class RestaurantExample {
    public static void main(String[] args) {
        // Create the receiver object
        Chef chef = new Chef();
        // Create the command object and associate it with the receiver
        OrderCommand command = new CookOrderCommand(chef);
        // Create the invoker and set the command
        Waiter waiter = new Waiter();
        waiter.setCommand(command);
        // Submit the order
        waiter.submitOrder();
    }
}
Java
command pattern - restaurant example - class diagram

Here’s another example of the Command design pattern using a remote control for controlling electronic devices:

// Receiver
class Television {
    public void turnOn() {
        System.out.println("Television: Turning on.");
    }
    public void turnOff() {
        System.out.println("Television: Turning off.");
    }
}
// Command
interface Command {
    void execute();
}
// Concrete Commands
class TurnOnCommand implements Command {
    private Television television;
    public TurnOnCommand(Television television) {
        this.television = television;
    }
    public void execute() {
        System.out.println("TurnOnCommand: Execute command.");
        television.turnOn();
    }
}
class TurnOffCommand implements Command {
    private Television television;
    public TurnOffCommand(Television television) {
        this.television = television;
    }
    public void execute() {
        System.out.println("TurnOffCommand: Execute command.");
        television.turnOff();
    }
}
// Invoker
class RemoteControl {
    private Command onCommand;
    private Command offCommand;
    public RemoteControl(Command onCommand, Command offCommand) {
        this.onCommand = onCommand;
        this.offCommand = offCommand;
    }
    public void turnOn() {
        System.out.println("RemoteControl: Turn on the device.");
        onCommand.execute();
    }
    public void turnOff() {
        System.out.println("RemoteControl: Turn off the device.");
        offCommand.execute();
    }
}
// Client
public class RemoteControlExample {
    public static void main(String[] args) {
        // Create the receiver object
        Television television = new Television();
        // Create the command objects and associate them with the receiver
        Command turnOnCommand = new TurnOnCommand(television);
        Command turnOffCommand = new TurnOffCommand(television);
        // Create the invoker and set the commands
        RemoteControl remoteControl = new RemoteControl(turnOnCommand, turnOffCommand);
        // Use the remote control to turn on and off the television
        remoteControl.turnOn();
        remoteControl.turnOff();
    }
}
Java

Command Pattern & Design Principles

The Command design pattern aligns with several important design principles, including:

  1. Single Responsibility Principle (SRP): The Command pattern adheres to the SRP by encapsulating a specific operation or behavior within a command object. Each command is responsible for executing a single action, promoting modular and focused design.
  2. Open/Closed Principle (OCP): The Command pattern supports the OCP by allowing new commands to be added without modifying existing code. The receiver and sender classes remain closed for modification, while new commands can be introduced by creating new command objects.
  3. Dependency Inversion Principle (DIP): The Command pattern follows the DIP by decoupling the sender and receiver through the use of command objects. The sender depends on the abstraction of the command interface or base class, rather than directly depending on the receiver.
  4. Encapsulation: The Command pattern encapsulates requests as objects, providing a clear and encapsulated interface for executing operations. It hides the details of how the request is executed, promoting encapsulation and information hiding.
  5. Separation of Concerns: The Command pattern separates the concerns of invoking a request and handling the request. The sender is responsible for invoking the command, while the receiver is responsible for executing the command. This separation allows for better organization and modularity of code.
  6. Flexibility and Extensibility: The Command pattern provides flexibility and extensibility by allowing new commands to be easily added and existing ones to be modified or replaced. This promotes code that is adaptable to changing requirements and promotes code reuse.

Pros & Cons

ProsCons
Loose coupling:
The sender does not need to know the specific details of how the request will be handled
Increased complexity:
It requires creating and managing command objects, which can lead to a more intricate design.
Flexibility and extensibility:
easy addition of new commands without modifying existing code.
Overhead
ReusabilityPotential memory usage:
Storing and managing command objects can consume memory, particularly if commands need to be stored in a queue or history for undo/redo functionality.
Undo/Redo functionality:
By storing and executing commands, it enables the ability to revert and reapply actions
Indirection: additional layer of indirection between the sender and receiver
Logging and auditing:
provides a centralized point to capture information about executed commands, enabling debugging, monitoring, and tracking of actions.
Increased code complexity due to introduction of large number of classes