Command Pattern - Java - Explained

Command Pattern - Java - Explained

Intent

The command pattern encapsulates a request as an object, thereby letting you parameterize other objects with different requests, queue or log requests, and support undoable operations.

This is a type of behavioral design pattern and it's also known as Action and Transaction.

Motivation behind the pattern/problem it solves?

Command patterns helps in decoupling invoker(remote control) and Receiver (Light,Fan,etc) with the help of command objects (LightOnCommand, FanOffCommand, etc).

Sometimes it's necessary to just trigger a specific action by abstracting who is calling that action or what a client is going to do after the action is done. This is possible using the command pattern.

It gives the ability to an object to encapsulate a request object and send it as a parameter to the executor. The executor has the ability to either stack this request or update the state of the requested action or execute the action requested by the request object.

Applicability of the Command design pattern

  1. Command Pattern provides the ability to parameterize objects depending on the action they must perform ( send action details as the object to a method argument.)

  2. specifies or adds in a queue and executes requests at different moments in time.

  3. offers support for very complex actions like memorizing the state of action and allowing going back to that state.

  4. decouples the object that invokes the action from the object that performs the action.

design problem scenario - command pattern

We are going to explore the simplest form of the example provided in the Head First design pattern book which is Simple Remote Control. Suppose you want to design a RemoteControl API where the expectation is to create a slot to configure different appliances.

image.png

We are going to implement simple light on and off functionality on top of it all other slots can be filled. The goal is to provide the ability to the remote control to configure different slots and respective ON and OFF buttons.

Solution

We would need to decouple the slots, button functionalities(commands), and the invoker of the functionalities (commands). By doing this decoupling we are providing the ability to business users to configure different slots with different commands.

Solution Design approach.

Please refer below design approach :

image.png

Open image in new tab here : [commandPatternSolutionDesignStructure.png](cdn.hashnode.com/res/hashnode/image/upload/.. align="left")

code : simple remote control

First, let's create light on and off business logic. (similar we can create other appliances logic like fan on/off, fridge on/off)

public class Light {

    public Light() {
    }

    public void on() {
        System.out.println("Light is on");
    }

    public void off() {
        System.out.println("Light is off");
    }
}

Now Wrap this business logic into the respective command object so that we can pass these actions as request parameters.

public interface Command {

    public void execute();
}

** LightOnCommand
public class LightOnCommand implements Command {
    Light light;
    public LightOnCommand(Light light) {
        this.light = light;
    }
    public void execute() {
        light.on();
    }
}

** LightOffCommand 
public class LightOffCommand implements Command {
    Light light;

    public void execute() {
        light.off();
    }

    public LightOffCommand(Light light) {
        this.light = light;
    }
}

Great !! we have created our command object, if you see we have an Interface command for every concrete command(LigntOffCommand, LightOnCommand) which has **execute ** method.
This is an abstraction of the command we have done, which means any command will have an execute method. So now it's easier to wrap any business logic and its invocation in the standard interface which is Command.

Now let's create our Simple Remote Control class whose responsibility is to provide slots and associate a command to it and also to execute command when the button is pressed.
This SimpleRemoteControl is the invoker class, which provides ability to the client class to associate command to the slot and then this Invoker is responsible to call actual business logic on the business entities configured in the Command object.

//
// This is the invoker
//

public class SimpleRemoteControl {
    Command slot;
    public SimpleRemoteControl() {}

    public void setCommand(Command command) {
        slot = command;
    }
    public void buttonWasPressed() {
        slot.execute();
    }
}

Now lets design out client class which create instance of the command object encapsulate it and send as a method parameter to be executed by the invoker class.

package com.headfirstdesignpatterns.command.simpleremote;


// this is a client class.
public class RemoteControlTest {
    public static void main(String[] args) {

        // receiver which is not directly linked to client. (we associate receiver with Concrete Command only)
        Light light = new Light();

        // creating a concrete command & setting its receiver - client class responsibility
        Command lightOn = new LightOnCommand(light);
        Command lightOff = new LightOffCommand(light);


        // this is invoker which holds the command and at certain point ask command to carry out request by calling out its execute method
        SimpleRemoteControl remote = new SimpleRemoteControl();

        // invoker holding command
        remote.setCommand(lightOff);
        // invoker ask to execute command
        remote.buttonWasPressed();

        // invoker holding command
        remote.setCommand(lightOn);
        // invoker ask to execute command
        remote.buttonWasPressed();


    }
}

complete github code

Structure Command Design Pattern

image.png

Open Image in new tab : [Structure_Command_Design_Pattern.png](cdn.hashnode.com/res/hashnode/image/upload/.. align="left")

  1. Client Class: This is responsible to create a command object and set that to the Invoker ( our simple example has only one slot defined for RemoteControl but improved version can have multiple slots and in client class we can assign slots along with commands to set to invoker class)

  2. Invoker: Remember Invoker is not creating the command class, it's just holding a reference of command set by the client and just triggers command execution (Remember Invoker is not directly invoking Light On method instead it just calls execute method on the Command)

  3. Command: It's an interface which declares simple method usually named execute.

  4. ConcreteCommand: Implements the Command by associating business entity and logic to exeute() method.

  5. Receiver: these are business object which needs to be executed on each command execution. These are wrapped in the Command Object.

Below is one more example which is scheduling the tasks using command pattern
The use of the Command pattern in this context allows for decoupling the task scheduling logic from the actual task execution logic.
It makes it easier to add new types of tasks without modifying the scheduler, adheres to the Open/Closed Principle, and provides a clear structure that is easy to understand and maintain.

import java.util.*;

// Command interface using Runnable for simplicity
interface Task extends Runnable {}

// Concrete Task: SendEmail - business logic wrapped under Task command object
class SendEmailTask implements Task {
    private String recipient;

    public SendEmailTask(String recipient) {
        this.recipient = recipient;
    }

    @Override
    public void run() {
        System.out.println("Sending email to " + recipient);
        // Logic to send email
    }
}

// Concrete Task: GenerateReport - business logic wrapped under Task command object
class GenerateReportTask implements Task {
    private String reportType;

    public GenerateReportTask(String reportType) {
        this.reportType = reportType;
    }

    @Override
    public void run() {
        System.out.println("Generating " + reportType + " report");
        // Logic to generate report
    }
}

// Concrete Task: DataSynchronization
//business logic wrapped under Task command object
class DataSynchronizationTask implements Task {
    @Override
    public void run() {
        System.out.println("Synchronizing data...");
        // Logic for data synchronization
    }
}

// Task Scheduler - Invoker
// Invoker executes the command when runTasks called by the client class
class TaskScheduler {
    private Queue<Task> taskQueue = new LinkedList<>();

    public void scheduleTask(Task task) {
        taskQueue.add(task);
        System.out.println("Task scheduled: " + task.getClass().getSimpleName());
    }

    public void runTasks() {
        while (!taskQueue.isEmpty()) {
            Task task = taskQueue.poll();
            task.run();
        }
    }
}

// client class - create instance of concrete command and set it to invoker list
public class BackendTaskSchedulerDemo {
    public static void main(String[] args) {
        TaskScheduler scheduler = new TaskScheduler();

        // Scheduling tasks
        scheduler.scheduleTask(new SendEmailTask("user@example.com"));
        scheduler.scheduleTask(new GenerateReportTask("Financial"));
        scheduler.scheduleTask(new DataSynchronizationTask());

        // Running scheduled tasks
        scheduler.runTasks();
    }
}

Pros and Cons

Pros

  1. Helps satisfy open/close principle: New commands can be introduced to existing logic without disturbing existing code.

  2. Decouples the classes that are invoking the operations and performing the operations.

  3. Allows creating a sequence of commands by providing a queue system.

Cons

  1. Code becomes complex to understand and new layer is added between sender and receiver.

  2. Design requires the introduction of many classes in the form of commands which means dealing with lot of classes.

Relations with other patterns

  1. Chain Of responsibilities handlers can be implemented using commands.

  2. Use Command and Memento together while implementing the redo where the command executes the command and Memento saves the state of an object before it is executed.

complete github code

Thanks For Reading Love IT Live IT Enjoy IT.