Command Pattern in Java – Encapsulate Requests as Reusable Objects

Illustration for Command Pattern in Java – Encapsulate Requests as Reusable Objects
By Last updated:

Introduction

The Command Pattern is a behavioral design pattern that encapsulates a request as an object, allowing parameterization of clients with queues, undo/redo operations, and logging of actions.

Why Command Pattern Matters

It decouples the object that invokes a command from the one that knows how to execute it. This is useful for executing commands later, storing history, batching tasks, or building macro commands.


Core Intent and Participants

  • Intent: Encapsulate a request as an object, thereby allowing for parameterization of clients with different requests, queuing, and logging.

Participants

  • Command: Declares the execution interface.
  • ConcreteCommand: Implements the command and delegates to a receiver.
  • Receiver: Knows how to perform the work.
  • Invoker: Triggers the command.
  • Client: Creates and configures commands.

UML Diagram (Text)

+---------+      +----------------+      +---------------+
| Client  |----->| ConcreteCommand|----->|   Receiver    |
+---------+      +----------------+      +---------------+
                     ^
                     |
               +------------+
               |  Command   |
               +------------+
                     ^
                     |
               +------------+
               |  Invoker   |
               +------------+

Real-World Use Cases

  • Remote controls (on/off, volume, channel)
  • GUI actions (undo, redo, copy, paste)
  • Task queues and schedulers
  • Macro recording systems
  • Game development (character actions, input mapping)
  • Workflow and rule engines

Java Implementation Strategy

Example: Remote Control

Step 1: Command Interface

public interface Command {
    void execute();
}

Step 2: Receiver

public class Light {
    public void turnOn() {
        System.out.println("Light is ON");
    }

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

Step 3: Concrete Commands

public class LightOnCommand implements Command {
    private Light light;

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

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

public class LightOffCommand implements Command {
    private Light light;

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

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

Step 4: Invoker

public class RemoteControl {
    private Command command;

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

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

Step 5: Client Code

public class CommandPatternDemo {
    public static void main(String[] args) {
        Light light = new Light();
        Command lightOn = new LightOnCommand(light);
        Command lightOff = new LightOffCommand(light);

        RemoteControl remote = new RemoteControl();

        remote.setCommand(lightOn);
        remote.pressButton();

        remote.setCommand(lightOff);
        remote.pressButton();
    }
}

✅ You can queue, undo, or log commands thanks to this structure.


Pros and Cons

✅ Pros

  • Decouples sender and receiver
  • Supports undo, logging, and queuing of requests
  • Adds extensibility without modifying existing code

❌ Cons

  • Can result in a large number of small classes
  • Slightly more complex architecture for simple actions

Anti-Patterns and Misuse

  • Using Command where simple method invocation suffices
  • Mixing logic into the invoker (violates SRP)
  • Not separating receiver logic from command

Command vs Strategy vs Observer

Pattern Goal Encapsulates Primary Use Case
Command Encapsulate and defer requests Action/Request Undo, logging, macros
Strategy Encapsulate algorithms Behavior Sorting, calculation logic
Observer Event-driven notifications State change Event handling, GUIs

Refactoring Legacy Code

Before

light.turnOn();
light.turnOff();

After (Using Command)

remote.setCommand(new LightOnCommand(light));
remote.pressButton();

✅ Enables reusability, undo, logging, and more.


Best Practices

  • Use command interface consistently across all commands
  • Favor immutability for command data (e.g., command parameters)
  • Batch and queue commands where needed (e.g., macro replay)
  • Use lambdas in Java 8+ to simplify command definitions

Real-World Analogy

Think of a restaurant waiter. You (client) place an order (command), the waiter (invoker) delivers it to the kitchen (receiver), which prepares the meal. The chef doesn’t know who ordered, just what to cook. The system is clean and decoupled.


Java Version Relevance

  • Java 8+: Use lambdas as commands for lightweight syntax
  • JavaFX: Uses command pattern for UI actions
  • Java Concurrency: Runnable and Callable are command-style interfaces

Conclusion & Key Takeaways

  • Command Pattern turns actions into objects that can be passed, queued, or undone.
  • Perfect for GUIs, input handlers, macro recording, and task queues.
  • Decouples sender from receiver and enhances maintainability.
  • Can be simplified with Java 8+ lambda expressions.

FAQ – Command Pattern in Java

1. What is the Command Pattern?

A pattern that encapsulates a request as an object.

2. When should I use it?

When you want to support undo, queuing, or execute commands at different times.

3. Is Command Pattern decoupled?

Yes. It separates the sender from the receiver.

4. Can I implement undo with this?

Yes. Store command history and implement a undo() method.

5. Can it be used in real-time apps?

Yes. Especially for game actions and UI input handling.

6. What’s the difference from Strategy Pattern?

Command encapsulates an action/request. Strategy encapsulates behavior/algorithm.

7. Is it used in Java standard libraries?

Yes. Runnable, Callable, and ActionListener use this concept.

8. Can commands be asynchronous?

Yes. Combine with executor services for async execution.

9. How do I log commands?

Store command objects in a list before executing.

10. Can I batch multiple commands together?

Yes. Use a MacroCommand that holds a list of commands.