Command Design Pattern in Java

In this article, we will do an in-depth exploration of Command Design Pattern in Java. So, let’s get started.

What is Command Design Pattern in Java ?

Command Design Pattern in Java helps to decouple the invoker and the receiver.Four terms associated with the command pattern are commandreceiverinvoker and client.

Command Design Pattern in Java
  • Command is an interface with execute method. It is the core of contract.
  • A client creates an instance of a command implementation and associates it with a receiver.
  • An invoker instructs the command to perform an action.
  • A Command implementation’s instance creates a binding between the receiver and an action.
  • Receiver is the object that knows the actual steps to perform the action.

Example: Command Design Pattern in Java

We will take an example of an universal remote which can operate on electronic devices like AC,TV and Washing Machine.For simplicity, we will consider only two commands i.e., On and Off.

Class Diagram

Command Design Pattern in Java

Java Code

// ElectronicDevice Interface

package com.design.command;

public interface ElectronicDevice {

    public void switchOn();
    public void switchOff();

}

// TV Class

package com.design.command;

public class TV implements ElectronicDevice {

    @Override
    public void switchOn() {
        System.out.println("TV switched On");
    }

    @Override
    public void switchOff() {
        System.out.println("TV switched Off");
    }

}

// WashingMachine Class

package com.design.command;

public class WashingMachine implements ElectronicDevice {

    @Override
    public void switchOn() {
        System.out.println("WashingMachine switched On");
    }

    @Override
    public void switchOff() {
        System.out.println("WashingMachine switched Off");
    }

}

// AC Class

package com.design.command;

public class AC implements ElectronicDevice {

    @Override
    public void switchOn() {
        System.out.println("AC switched On");
    }

    @Override
    public void switchOff() {
        System.out.println("AC switched Off");
    }

}

// Command Interface

package com.design.command;

public interface Command {

    public void execute();

}

// OnCommand Class

package com.design.command;

public class OnCommand implements Command {

    private ElectronicDevice electronicDevice;

    @Override
    public void execute() {
        electronicDevice.switchOn();
    }

    public OnCommand(ElectronicDevice electronicDevice) {
        this.electronicDevice = electronicDevice;
    }

}

// OffCommand Class

package com.design.command;

public class OffCommand implements Command {

    private ElectronicDevice electronicDevice;

    @Override
    public void execute() {
        electronicDevice.switchOff();
    }

    public OffCommand(ElectronicDevice electronicDevice) {
        this.electronicDevice = electronicDevice;
    }

}

// UniversalRemoteControl Class

package com.design.command;

public class UniversalRemoteControl {

    private Command command;

    public UniversalRemoteControl(Command command) {
        this.command = command;
    }

    public void pressButton() {
        command.execute();
    }

    public Command getCommand() {
        return command;
    }

    public void setCommand(Command command) {
        this.command = command;
    }

}

// CommandDemo Class

package com.design.command;

public class CommandDemo {

    public static void main(String[] args) {
        ElectronicDevice tv = new TV();
        //switch on TV
        Command switchOnTv = new OnCommand(tv);
        UniversalRemoteControl control = new UniversalRemoteControl(switchOnTv);
        control.pressButton();
        //switch off TV
        Command switchOffTv = new OffCommand(tv);
        control.setCommand(switchOffTv);
        control.pressButton();

        //switch on AC
        ElectronicDevice ac = new AC();
        Command switchOnAC = new OnCommand(ac);
        control.setCommand(switchOnAC);
        control.pressButton();
        //switch off AC
        Command switchOffAC = new OffCommand(ac);
        control.setCommand(switchOffAC);
        control.pressButton();

        //switch on AC
        ElectronicDevice washingMachine = new WashingMachine();
        Command switchOnWM = new OnCommand(washingMachine);
        control.setCommand(switchOnWM);
        control.pressButton();
        //switch off AC
        Command switchOffWM = new OffCommand(washingMachine);
        control.setCommand(switchOffWM);
        control.pressButton();
    }

}

// Output
TV switched On
TV switched Off
AC switched On
AC switched Off
WashingMachine switched On
WashingMachine switched Off

Use-Cases: Command Design Pattern in Java

  1. GUI Applications: Implementing GUI operations like buttons, menus, and keyboard shortcuts, where each action is encapsulated as a command.
  2. Undo/Redo Functionality: Enabling users to undo and redo operations by storing commands and their parameters.
  3. Remote Control Devices: Creating universal remote controls that execute various commands on different devices.
  4. Transaction Management: Handling database transactions as commands, allowing easy rollback and commit functionalities.
  5. Batch Processing: Orchestrating batch processing tasks with the ability to schedule, pause, resume, and cancel.
  6. Multi-Level Menus: Designing complex menus where each menu item is represented by a command.

Pros: Command Design Pattern in Java

  1. Decoupling: Separating sender and receiver of a command, reducing direct dependencies and promoting flexibility.
  2. Undo/Redo: Facilitating the implementation of undo and redo functionality with a history of executed commands.
  3. Flexibility: Easily adding new commands without modifying existing client code.
  4. Reusability: Commands can be reused across different contexts and scenarios.
  5. Complex Operations: Breaking down complex operations into smaller, manageable command objects.
  6. Logging and Auditing: Logging executed commands for auditing and debugging purposes.

Cons: Command Design Pattern in Java

  1. Increased Complexity: Introducing multiple command classes and their management might add complexity.
  2. Abstraction Overhead: Creating numerous command classes for simple tasks could result in abstraction overhead.
  3. Memory Usage: Storing command objects in memory could impact memory usage, especially in memory-intensive applications.
  4. Potential Overhead: Adding an additional layer of abstraction could lead to a slight performance overhead.

Best Practices: Command Design Pattern in Java

  1. Clear Command Interface: Define a clear interface for all command classes, typically with an execute() method.
  2. Single Responsibility Principle: Ensure each command class has a single responsibility and encapsulates a specific action.
  3. Invoker Separation: Keep invoker and command objects separate to maintain clear responsibilities.
  4. Use Composite Pattern: Consider using the Composite pattern to group multiple commands into a composite command.
  5. Command Factory: Utilize a command factory to create instances of command objects with appropriate parameters.
  6. Testing: Thoroughly test command classes to ensure they perform the intended actions correctly.
  7. Command History: Implement a history mechanism to support undo and redo functionality if required.
  8. Documentation: Document each command class, its purpose, and its interaction with other classes.
  9. Use Enums or Constants: Use enums or constants to represent different types of commands for better readability.
  10. Avoid Over-Engineering: Use the Command pattern only when necessary, avoiding over-engineering for simple scenarios.

Conclusion: Command Design Pattern in Java

Command Design Pattern in Java provides a powerful way to decouple the sender and receiver of commands, promoting flexibility and reusability in various scenarios. By encapsulating operations as command objects, this pattern enables the implementation of undo/redo functionality, remote control devices, GUI interactions, and more. While offering benefits like improved code organization, reduced coupling, and complex task breakdown, it’s essential to consider potential downsides such as increased complexity and abstraction overhead. Adhering to best practices, like defining clear interfaces, separating responsibilities, and thoughtful testing, ensures the successful application of the Command pattern in Java. Remember to strike a balance between using the pattern to enhance code structure without over-engineering for simpler use cases.

Must Read Design Pattern Articles: 

Recommended Books:

Leave a Reply

Your email address will not be published. Required fields are marked *