DEV Community

Harsh Mange
Harsh Mange

Posted on • Originally published at harshmange.hashnode.dev on

What is the Command Design Pattern?

The Command Design Pattern is a behavioural design pattern that allows you to encapsulate requests or commands as objects, thereby allowing you to parameterize clients with different requests and queue or log requests for later use. It separates the request for action from its execution, providing a flexible and decoupled way of performing actions.

How it works?

In the Command Design Pattern, a request is encapsulated in an object that acts as a command. The command object contains all the necessary information required for executing the request, including the object on which the request is to be performed, the method to be called and the arguments to be passed to the method. The client that creates the command object does not need to know any of these details. It simply needs to know the command interface and how to invoke the command.

The command object is then passed to an invoker object, which can be a queue, a log or a menu. The invoker holds a reference to the command object and can execute the command at any time by calling its execute method.

Structure

+----------------+ +------------------+
| Command |<------| ConcreteCommand |
+----------------+ +------------------+
| + execute() | | + ConcreteCommand |
+----------------+ | (receiver) |
                          +------------------+
                                 /\
                                 ||
                          +------------------+
                          | Receiver |
                          +------------------+
                          | + action() |
                          +------------------+

Enter fullscreen mode Exit fullscreen mode

Example

Let's consider an example of a home automation system that has a remote control. The remote control has a set of buttons, each of which is associated with a different device such as a TV, a stereo or a fan. When a button is pressed, the remote control should be able to perform the appropriate action on the associated device.

We can use the Command Design Pattern to implement this functionality. We can define a command interface with an execute method that takes no parameters. Each command object can represent a different action that can be performed on a device, such as turning it on or off, changing the volume or changing the channel.

We can then create concrete command classes for each of these actions. These concrete command classes will encapsulate the logic for performing the action on the associated device. For example, the TurnOnCommand class can have a reference to the TV object and call its turnOn method when the execute method is called.

We can also create an invoker object, which can be the remote control itself. The remote control can have a set of buttons, each of which is associated with a different command object. When a button is pressed, the remote control can call the execute method on the associated command object, which in turn will perform the appropriate action on the associated device.

Implementation

Here's an example of how to implement the Command Design Pattern in Java:

Let's say we have a simple application that can perform addition, subtraction, multiplication, and division on two numbers. We want to implement the application using the Command Design Pattern.

First, let's define the Receiver interface, which represents the object that will perform the actual calculations:

public interface Calculator {
    public void add(double x, double y);
    public void subtract(double x, double y);
    public void multiply(double x, double y);
    public void divide(double x, double y);
}

Enter fullscreen mode Exit fullscreen mode

Next, we define the Command interface and its implementations:

public interface Command {
    public void execute();
}

public class AddCommand implements Command {
    private Calculator calculator;
    private double x;
    private double y;

    public AddCommand(Calculator calculator, double x, double y) {
        this.calculator = calculator;
        this.x = x;
        this.y = y;
    }

    public void execute() {
        calculator.add(x, y);
    }
}

public class SubtractCommand implements Command {
    private Calculator calculator;
    private double x;
    private double y;

    public SubtractCommand(Calculator calculator, double x, double y) {
        this.calculator = calculator;
        this.x = x;
        this.y = y;
    }

    public void execute() {
        calculator.subtract(x, y);
    }
}

public class MultiplyCommand implements Command {
    private Calculator calculator;
    private double x;
    private double y;

    public MultiplyCommand(Calculator calculator, double x, double y) {
        this.calculator = calculator;
        this.x = x;
        this.y = y;
    }

    public void execute() {
        calculator.multiply(x, y);
    }
}

public class DivideCommand implements Command {
    private Calculator calculator;
    private double x;
    private double y;

    public DivideCommand(Calculator calculator, double x, double y) {
        this.calculator = calculator;
        this.x = x;
        this.y = y;
    }

    public void execute() {
        calculator.divide(x, y);
    }
}

Enter fullscreen mode Exit fullscreen mode

Next, we define the Invoker class, which will execute the commands:

public class CalculatorInvoker {
    private List<Command> commands = new ArrayList<>();

    public void setCommand(Command command) {
        commands.add(command);
    }

    public void executeCommands() {
        for (Command command : commands) {
            command.execute();
        }
        commands.clear();
    }
}

Enter fullscreen mode Exit fullscreen mode

Finally, we can use the classes to perform the calculations:

public class CalculatorApplication {
    public static void main(String[] args) {
        Calculator calculator = new CalculatorImpl();
        CalculatorInvoker invoker = new CalculatorInvoker();

        // Add 2 + 3
        invoker.setCommand(new AddCommand(calculator, 2, 3));
        invoker.executeCommands();

        // Subtract 5 - 1
        invoker.setCommand(new SubtractCommand(calculator, 5, 1));
        invoker.executeCommands();

        // Multiply 4 * 6
        invoker.setCommand(new MultiplyCommand(calculator, 4, 6));
        invoker.executeCommands();

        // Divide 10 / 2
        invoker.setCommand(new DivideCommand(calculator, 10, 2));
        invoker.executeCommands();
    }
}

Enter fullscreen mode Exit fullscreen mode

In this example, the CalculatorInvoker is the Invoker class, the Command interface and its implementations represent the different operations that can be performed on the Calculator object, and the Calculator interface is the Receiver interface.

Advantages

The Command Design Pattern has several advantages, including:

  • It provides a way of decoupling the requester of an action from the object that actually performs the action.

  • It provides a way of parameterizing objects with different requests.

  • It provides a way of queuing or logging requests for later execution.

  • It provides a way of implementing undo or redo functionality by storing the command objects.

Disadvantages

The Command Design Pattern has some potential disadvantages, including:

  • It can result in a large number of command classes, especially if there are many actions that can be performed on an object.

  • It can introduce additional overhead, especially if there are many objects and commands.

Conclusion

The Command Design Pattern is a useful pattern for encapsulating requests as objects, thereby allowing you to parameterize clients with different requests and queue or log requests for later use. It provides a flexible and decoupled way of performing actions and can be used in a wide range of applications.

Top comments (0)