Command Design Pattern

The Command Design Pattern wraps a request in an object as command and pass it to a command invoker object. When invoker receives a request, it looks for the appropriate object which is capable of handling this request and passes the command to the corresponding object for execution. This allows clients to be parameterized with different requests.

In command pattern, a client just gives instructions to invokes without knowing how it is going to be executed. It supports decoupling between a client object which request a task and the receiver object which actually performs the task. This pattern is not focused on the sequence of the tasks stored, but on hiding the details/implementation of the actions performed.

  • Define a Command interface having an execute method execute().
  • All command objects must implements a Command interface. The execute method delegates the request to a receiver to execute the command.
  • A receiver class holds the logic of performing any specific task requested as command. It is called from execute method of command object.
  • The client creates a set of command objects and associates receiver with it. Client passes commands to invoker to store it. Later, client calls invoker to execute the commands.


Structure of the Command Design Pattern

The Command Design Pattern consists of the following components:
  • Command Interface : This interface declares a method execute() that concrete command classes must implement. It represents the abstract command that can be executed.

  • Concrete Command : The Command interface is implemented by Concrete Command classes. They include the data needed to carry out a request and encapsulate it as an object.

  • Invoker : The invoker is responsible for invoking the command. It holds a reference to a command and triggers its execution.

  • Receiver : When a command is executed, the receiver is the thing that actually gets the job done. It is capable of completing the request included in the command.

  • Client : The client creates and configures concrete command objects and associates them with specific receivers. It is also responsible for associating commands with invokers.

Advantages of Command Design Pattern

  • Decoupling of Sender and Receiver : One of the primary advantages of the Command pattern is the decoupling of the sender and receiver. The sender (client) does not need to know the details of how a command is executed or which object will perform the action.

  • Flexibility in Command Execution : By enabling clients to parameterize objects with various commands, the Command pattern offers clients flexibility in command execution. Clients can now dynamically switch between multiple commands thanks to this.

  • Encapsulation of Commands : Commands are encapsulated as objects, allowing for easy extension and organization of functionality. Each command class encapsulates a specific action, promoting a modular and maintainable design.

  • Easy Addition of New Commands : The extensibility of the Command pattern allows for the addition of new commands without altering existing client code. This enables the incorporation of new behaviors into the system seamlessly.

  • Enhanced Separation of Concerns : By separating the execution of commands from the specifics of how actions are carried out, the Command pattern promotes an improved separation of concerns. This division contributes to a more modular and comprehensible design.

  • Improved Maintainability : The separation of concerns and encapsulation of commands lead to better maintainability. Features can be added or modified without causing disruptions to the current codebase, enhancing the overall maintainability of the system.

When we should use Command Pattern

  • When we want to keep historical records of the actions performed on an object, where every action is implemented as a command.
  • When we implement a sequence of tasks as workflow. We can model every task as a command and execute them in a specific sequence.
  • When we want to implement object oriented call back.
  • When we want to decouple the invoker and the receiver.

Implementation of Command Design Pattern

  1. Define a Command interface having an execute method execute().
  2. All command objects must implements a Command interface. The execute method delegates the request to a receiver to execute the command.
  3. A receiver class holds the logic of performing any specific task requested as command. It is called from execute method of command object.
  4. The client creates a set of command objects and associates receiver with it. Client passes commands to invoker to store it. Later, client calls invoker to execute the commands.
Command  Design Pattern UML Diagram Command.java
public interface Command {
   public void execute();
}

DrawCircle and DrawRectangle are concrete implementation of Command interface.

DrawCircle .java
public class DrawCircle implements Command {
    private ShapeDrafter drafter;
 
    public DrawCircle(ShapeDrafter drafter){
        this.drafter = drafter;
    }
 
    public void execute(){
        drafter.drawCircle();
    }
}

DrawRectangle.java
public class DrawRectangle implements Command {
    private ShapeDrafter drafter;
 
    public DrawRectangle(ShapeDrafter drafter){
        this.drafter = drafter;
    }
 
    public void execute(){
        drafter.drawRectangle();
    }
}

ShapeDrafter is the receiver class which draws circle and rectangle.

ShapeDrafter.java
public class ShapeDrafter {
    public void drawRectangle(){
        System.out.println("Drawing a Rectangle on Screen");
    }
 
    public void drawCircle(){
        System.out.println("Drawing a Circle on Screen"); 
    }
}

CommandInvoker.java
import java.util.List;
import java.util.ArrayList;

public class CommandInvoker {
    private List<Command> commandList = new ArrayList<Command>();
 
    public void addCommand(Command c){
        commandList.add(c);
    }
 
    public void executeCommands(){
        for(Command c : commandList){
            c.execute();
        }
    }
}

CommandPatternExample class creates command objects and pass it to CommandInvoker for execution.

public class CommandPatternExample {
    public static void main(String args[]){
        ShapeDrafter drafter = new ShapeDrafter();
 
        Command rectangleCommand = new DrawRectangle(drafter);
        Command circleCommand = new DrawCircle(drafter);
 
        CommandInvoker invoker = new CommandInvoker();
        invoker.addCommand(circleCommand);
        invoker.addCommand(rectangleCommand);
     
        invoker.executeCommands();
    }
}

Output

Drawing a Circle on Screen
Drawing a Rectangle on Screen

Best Practices of Command Design Pattern

To get the most out of the Command Design Pattern, adhere to recommended practices that support an adaptable and maintained implementation:
  • The Command interface should have a clear and concise definition. It should declare only the methods required for command execution to ensure a consistent interface for all concrete commands.

  • For flexible execution, use the Command interface's parameters. This makes commands more versatile by allowing them to be parameterized with various arguments.

  • Construct concrete command classes that include the logic of the receiver. Better structure and encapsulation of action connected to a particular command are made possible by this.

  • Design the receiver class to perform the actual work associated with the command. Ensure that the receiver encapsulates the details of the action and is capable of handling command execution.

  • Think about adding methods like undo() to the Command interface to provide undo/redo capability. The logic required to reverse the command that was executed can then be provided by concrete command classes.

  • Keep command classes focused on a specific action to avoid creating overly complex commands. Each command should represent a single responsibility to adhere to the Single Responsibility Principle.

  • Use command queues to enable the queuing and sequential execution of commands. In situations when the sequence in which commands are executed is critical, this can be helpful.

  • Use dependency injection to inject receivers into command objects. This promotes flexibility and makes it easier to replace or switch receivers without modifying the command classes.

  • Implement composite commands using the Composite Pattern when dealing with sequences of commands as a single unit. This enables the creation of macro commands that consist of multiple sub-commands.
Related Topics
Strategy Design Pattern
Factory Design Pattern
Abstract Factory Design Pattern
Composite Design Pattern
Observer Design Pattern
List of Design Patterns