Command Design Pattern in Java: Complete Guide with Real-World Examples

Command Design Pattern in Java: Complete Guide with Examples (2026)

Command Design Pattern in Java: A Complete Guide with Real-World Examples

Learn the Command design pattern in Java with simple explanations and practical examples. This guide covers how a request becomes an object, with before-and-after code for beginners and experts.

Imagine you are using a TV remote. You press the power button, and the TV turns on. You press the volume button, and the sound increases. You do not need to know how the TV works internally. You simply press a button, and the remote handles the request. Now imagine building an application where different actions need to be executed — sending an email, processing a payment, creating an order. If every button or service directly calls the logic of the object it controls, the code can quickly become difficult to maintain. This is where the Command Design Pattern becomes useful.

Why the Command Pattern Still Matters in Java

Imagine you are using a TV remote. You press the power button, and the TV turns on. You press the volume button, and the sound increases. You do not need to know how the TV works internally. You simply press a button, and the remote handles the request.

Now imagine building an application where different actions need to be executed, such as sending an email, processing a payment, creating an order, or turning on a device. If every button or service directly calls the logic of the object it controls, the code can quickly become difficult to maintain.

The Command Design Pattern solves this by converting a request into a separate object. This object contains the information needed to perform an operation. Instead of calling a method directly, the requester works with a command object. The command decides which operation should be executed and delegates the work to the appropriate object.

Whether you're building UI actions, job schedulers, workflow engines, or task queues, the Command pattern keeps your invoker decoupled from the receiver and makes actions first-class objects.

What Is the Command Design Pattern?

"Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations."

In plain words: the Command Pattern separates the person or object requesting an action from the object performing that action. Instead of calling a method directly, the requester works with a command object. The command decides which operation should be executed and delegates the work to the appropriate object.

For example, when you click the Submit button on a payment page, the UI does not need to know all the internal details of payment processing. The action can be represented as a command, which is then passed to the component responsible for executing it.

The Three Key Roles

Role Responsibility Example
Command Defines a common operation for all commands Command interface
Receiver Contains the actual business logic Light
Invoker Triggers the command without knowing its implementation RemoteControl
Command Design Pattern flow in Java The Client creates a Concrete Command. The Invoker calls execute on the Command interface. The Concrete Command forwards the request to the Receiver, which performs the operation. Client Creates a command Concrete Command Stores the request Receiver Performs the work Command Interface execute() Invoker Triggers the command Calls execute() Delegates work Request → Command → Receiver The invoker triggers the action without knowing its implementation.

Command Pattern: the request and its execution logic are separated.

How to read this diagram (for freshers): The top row shows the flow — a Client creates a Concrete Command and connects it to the Invoker. When the invoker is triggered, it calls execute() on the Command interface. The Concrete Command then delegates the actual work to the Receiver, which performs the operation. The invoker never needs to know which receiver is behind the command.

Understanding the Main Components

Before writing code, let's understand the responsibilities of each component.

1. Command Interface

This defines a common method, usually called execute(). All concrete commands follow this contract, so the invoker can work with them without knowing their specific implementation.

2. Concrete Command

This class implements the Command interface. It connects a particular request to the object that knows how to perform it.

3. Receiver

The receiver contains the actual business logic. For example, a Light object knows how to turn a light on or off.

4. Invoker

The invoker triggers a command. It might be a remote-control button, a menu option, a scheduler, or a job queue. It does not need to know how the receiver performs the operation.

5. Client

The client creates the command, supplies the receiver when needed, and connects the command to the invoker.

The Five Players

Role Responsibility Example
Command Interface Defines a common method, usually execute() Command
Concrete Command Connects a particular request to the object that knows how to perform it LightOnCommand
Receiver Contains the actual business logic Light
Invoker Triggers the command without knowing its implementation RemoteControl
Client Creates the command, supplies the receiver, and connects the command to the invoker Main

Real-World Example: A Smart Home Remote

Let's use a familiar example. Imagine a smart home remote that can turn a light on or off. Later, we might add a fan, television, or air conditioner.

Without the Command Pattern, the remote might contain a growing collection of conditions for every device. Every time a new device is added, we may need to modify the remote's code.

With the Command Pattern, each action gets its own command class. The remote only needs to trigger the command. It does not need to know whether the command controls a light, a fan, or another device.

Why the Pattern Fits Here

Problem Without Command With Command Real-World Parallel
Remote grows with every device Each action is a separate command class A TV remote with a fixed button layout
Remote knows device internals Remote only calls execute() You press a button without knowing the TV circuitry
Adding a fan requires editing the remote Adding a fan means writing a new command A universal remote learns a new device code

Complete Java Example

We will create the following classes:

  • Command — the common contract.
  • Light — the receiver.
  • LightOnCommand — turns the light on.
  • LightOffCommand — turns the light off.
  • RemoteControl — the invoker.
  • Main — creates and connects the objects.

Step 1: Create the Command Interface

public interface Command {
    void execute();
}

This interface provides a common method for every command. The remote can now trigger any object that implements Command.

Step 2: Create the Receiver

public class Light {

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

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

The Light class contains the actual operations. It does not need to know anything about the remote control or command interface.

Step 3: Create the Concrete Commands

public class LightOnCommand implements Command {

    private final Light light;

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

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

The LightOnCommand stores a reference to the receiver. When its execute() method is called, it asks the light to turn on.

Now create the command for turning the light off:

public class LightOffCommand implements Command {

    private final Light light;

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

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

Notice that both classes implement the same interface. Their behavior differs, but the way the remote invokes them remains the same.

Step 4: Create the Invoker

public class RemoteControl {

    private Command command;

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

    public void pressButton() {
        if (command == null) {
            throw new IllegalStateException(
                "No command has been configured"
            );
        }

        command.execute();
    }
}

The remote doesn't contain light-specific logic. It only stores a command and invokes it when the button is pressed.

Step 5: Run the Example

public class Main {

    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();
    }
}

Output:

Light is ON
Light is OFF

Run the example and follow the execution. The remote calls execute(), the concrete command receives that call, and the command delegates the actual work to the light.

That separation is the main idea behind the Command Pattern.

For Beginners: The Restaurant Order Analogy

Think of a restaurant. You (the client) tell the waiter (the invoker) what you want. The waiter writes your order on a slip (the command) and hands it to the kitchen (the receiver). The waiter doesn't cook the food — the kitchen does. And you don't need to know how the kitchen works. The order slip is the command object: it carries the request from the invoker to the receiver.

Command is the order slip. The waiter is the invoker. The kitchen is the receiver.

What Happens When We Add Another Device?

Suppose we now want to support a fan. We can create a Fan receiver and a FanOnCommand that implements the same Command interface.

public class Fan {

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

public class FanOnCommand implements Command {

    private final Fan fan;

    public FanOnCommand(Fan fan) {
        this.fan = fan;
    }

    @Override
    public void execute() {
        fan.turnOn();
    }
}

We don't need to add a new condition to RemoteControl. We simply configure it with the new command:

Fan fan = new Fan();

remote.setCommand(new FanOnCommand(fan));
remote.pressButton();

The output is:

Fan is ON

This is a practical benefit of the pattern: new actions can be introduced without making the invoker understand every type of receiver.

Important: The invoker never changes when you add a new command. That's the Open/Closed principle in action — RemoteControl is closed for modification but open for extension through new Command implementations.

Real-World Software Example: Processing an Order

The smart home example explains the pattern, but where can it help in business applications?

Consider an e-commerce application. After a customer places an order, the system may need to reserve inventory, send a confirmation email, or initiate a payment. These are different actions, and different components may be responsible for performing them.

A command can represent a specific action, while a handler performs the work. The caller can trigger the command without containing all the business logic itself.

Here is a simplified example that follows the same design.

public interface OrderCommand {
    void execute();
}

public class SendOrderConfirmationCommand
        implements OrderCommand {

    private final String orderId;

    public SendOrderConfirmationCommand(String orderId) {
        this.orderId = orderId;
    }

    @Override
    public void execute() {
        System.out.println(
            "Sending confirmation for order: " + orderId
        );
    }
}

public class OrderActionInvoker {

    public void submit(OrderCommand command) {
        command.execute();
    }
}

public class OrderApplication {

    public static void main(String[] args) {

        OrderCommand command =
            new SendOrderConfirmationCommand("ORD-1001");

        OrderActionInvoker invoker =
            new OrderActionInvoker();

        invoker.submit(command);
    }
}

Output:

Sending confirmation for order: ORD-1001

This example is intentionally small. In a real order service, the command would usually call an injected application service, and that service would handle the actual email delivery or order operation.

Important: For important payment or order workflows, do not assume that wrapping an operation in a command automatically makes it reliable. You still need appropriate transaction boundaries, retry rules, idempotency, error handling, and monitoring.

For Experts: Command Pattern vs Strategy (Know the Difference)

This is a common interview trap.

Aspect Command Pattern Strategy Pattern
Main purpose Represents a request as an object Chooses an algorithm or behavior
Typical example Remote-control button or queued job Different payment fee or discount calculations
Request handling Can support queuing, logging, and undo when designed for it Usually focuses on selecting and executing a behavior
Key question Which action should be executed? Which algorithm should be used?

Rule of thumb: If you need to choose between different discount calculation algorithms, Strategy is a natural fit. If you need to represent a user action that can be queued, logged, or potentially undone, Command may be a better fit.

How to Use the Command Pattern in Spring Boot

In Spring Boot, we can use the same idea with dependency injection. Each command implementation can be a Spring bean, and a service can invoke a command through a common interface.

For example, imagine an application that supports multiple notification actions. The following simplified implementation shows how Spring can manage a command bean.

Step 1: Define the Command Interface

public interface NotificationCommand {
    void execute(String recipient);
}

Step 2: Create a Concrete Command

import org.springframework.stereotype.Component;

@Component
public class EmailNotificationCommand
        implements NotificationCommand {

    @Override
    public void execute(String recipient) {
        System.out.println(
            "Sending email notification to: " + recipient
        );
    }
}

Step 3: Create a Service That Invokes the Command

import org.springframework.stereotype.Service;

@Service
public class NotificationService {

    private final NotificationCommand notificationCommand;

    public NotificationService(
            NotificationCommand notificationCommand) {
        this.notificationCommand = notificationCommand;
    }

    public void sendNotification(String recipient) {
        notificationCommand.execute(recipient);
    }
}

Spring Boot injects the command implementation into the service. When the service calls sendNotification(), the command executes the requested action.

Important: This is a small demonstration of the command concept with dependency injection. If you have multiple NotificationCommand implementations, Spring will need an explicit way to choose the correct one, such as a qualifier, a command registry, or a map of named handlers. For a production application, connect the command to a real notification service instead of printing a message.

Command Pattern with Undo Operations

One interesting use of the Command Pattern is the ability to support undo operations. Since an action is represented as an object, the command can also provide a way to reverse its work when the operation is reversible.

For example, turning on a light can be reversed by turning it off. We can extend the interface:

public interface UndoableCommand {
    void execute();
    void undo();
}

Then implement both operations in the command:

public class ReversibleLightOnCommand
        implements UndoableCommand {

    private final Light light;

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

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

    @Override
    public void undo() {
        light.turnOff();
    }
}

The invoker can keep a reference to the last command and call undo() when the user requests it.

This approach can be useful in editors, drawing applications, workflow tools, and other systems where actions can be reversed. However, not every action can be safely undone. Sending an email or completing an external payment, for example, cannot always be reversed by simply calling an opposite method.

Command Pattern vs Strategy Pattern

Both patterns use interfaces and interchangeable implementations, so they can look similar. Their main difference is the problem they are intended to solve.

Point Command Pattern Strategy Pattern
Main purpose Represents a request as an object Chooses an algorithm or behavior
Typical example Remote-control button or queued job Different payment fee or discount calculations
Request handling Can support queuing, logging, and undo when designed for it Usually focuses on selecting and executing a behavior
Key question Which action should be executed? Which algorithm should be used?

For example, if you need to choose between different discount calculation algorithms, Strategy is a natural fit. If you need to represent a user action that can be queued, logged, or potentially undone, Command may be a better fit.

Command Pattern vs Observer Pattern

The Command Pattern represents an action that can be invoked. The Observer Pattern lets multiple subscribers receive notifications when an event or state change occurs.

Imagine clicking a button to place an order. A command could represent the request to place the order. After the order is successfully placed, an event could notify other components that the order status has changed.

In larger systems, commands and events are often used together, but they serve different purposes.

Advantages and Disadvantages

Advantages Disadvantages
  • Loose coupling: the invoker does not need to know the receiver's implementation
  • Easy to extend: new actions can be added through new command implementations
  • Supports queuing: command objects can be stored and executed later
  • Supports logging: the system can record which commands were requested and when they ran
  • Can support undo: reversible commands can implement an undo operation
  • Useful for testing: the invoker can be tested using a small fake command
  • More classes: every distinct action may require its own command implementation
  • Additional abstraction: simple applications may not need this extra layer
  • State management: commands that store mutable state can become difficult to reuse safely
  • Undo is not always possible: external or irreversible actions may not have a true reverse operation
  • Asynchronous execution needs more design: queuing a command does not automatically provide retries, durability, or exactly-once execution

Frequently Asked Interview Questions

1. What is the Command Design Pattern in Java?

It is a behavioral design pattern that represents a request as an object. It separates the requester from the component that performs the operation.

2. Is Command a behavioral design pattern?

Yes. It focuses on how requests and operations are represented and executed between objects.

3. What are the main components of the Command Pattern?

The usual components are the Command interface, Concrete Command, Receiver, Invoker, and Client.

4. Why do we use the execute() method?

It provides a common operation through which the invoker can trigger different command implementations without knowing their individual details.

5. Can the Command Pattern support undo and redo?

Yes, when commands store enough information and implement appropriate reversal or replay behavior. Some operations cannot be reversed safely.

6. What is the difference between Command and Strategy?

Command represents a request or action as an object. Strategy encapsulates interchangeable algorithms or behaviors.

7. Can Command objects be stored in a queue?

Yes. A queue can hold commands for later execution. In a distributed application, durable storage, serialization, retries, idempotency, and failure recovery must be handled separately.

8. How is the Command Pattern useful in Spring Boot?

It can separate application actions from the components that invoke them. Spring can create and inject command implementations, while a service or registry selects the correct command.

9. Is the Command Pattern always better than a direct method call?

No. It is most useful when actions need to be represented, selected, queued, logged, or managed independently. For simple operations, a direct method call is often clearer.

10. Where might you use the Command Pattern in a real project?

Common examples include UI actions, job scheduling, workflow operations, task queues, order-processing actions, and operations that need an undo mechanism.

Your Command Pattern Checklist

Before turning every method call into a command object, ask:

  • Do I need to represent an action as an object? If actions need to be queued, logged, or undone, yes.
  • Will the invoker change when new actions arrive? If yes, a command decouples it from the receiver.
  • Do I need undo or replay? Reversible actions can implement undo() alongside execute().
  • Is the action simple and stable? For a single, never-changing method call, a command may be overkill.
  • How will I handle failures? Define retry, idempotency, and error-handling rules explicitly.

Start small. Refactor one growing invoker. The maintainability gains compound.

Best Practices for Experienced Java Developers

  • Keep commands focused: A command should represent a clear action rather than becoming a large class that handles unrelated responsibilities.
  • Prefer constructor injection: In Spring Boot, inject the services a command needs rather than looking them up globally.
  • Be careful with mutable state: A command stored in a queue should contain the necessary request data and should not depend on unsafe shared state.
  • Design retries deliberately: If an action can be retried, consider idempotency keys, duplicate requests, timeouts, and failure handling.
  • Separate business logic from orchestration: The command can coordinate an action while dedicated services perform the actual business operations.
  • Do not overengineer: If a simple method call already solves the problem cleanly, a command class may add unnecessary complexity.
  • Secure command execution: Validate inputs and check authorization before executing sensitive actions, especially in payment and account-management systems.

Final Thoughts

The Command Design Pattern is useful when an application needs to separate the request for an action from the logic that performs it. Instead of making the invoker understand every operation, we give each action a command object with a common interface.

Start with the remote-control example until the roles of the command, invoker, and receiver become clear. Then look for places in your own Java projects where requests need to be queued, selected, logged, or handled independently.

Remember, a design pattern is not a rule that must be applied everywhere. Use the Command Pattern when its separation of responsibilities makes your code easier to extend, test, and maintain.

Related topics: Java Design Patterns: Beginner's Guide, Strategy Design Pattern in Java, Observer Design Pattern in Java, Spring Boot Filters and Interceptors, SOLID Principles with Spring Boot Examples

Practice task: Extend the smart home example with a FanOffCommand and add a history list to the remote so it records the commands that have been executed. Think about how you would implement undo without placing device-specific logic inside the remote.

No comments: