Command Design Pattern in Java: Encapsulating Requests as Objects

Description / Meta Description

Learn the Command Design Pattern in Java with practical examples. Understand how Command encapsulates requests as objects, enables undo operations, task scheduling, workflow execution, and decouples request senders from receivers. Discover real-world examples from Spring Batch, Quartz Scheduler, workflow engines, and enterprise applications.


Command Design Pattern in Java: Encapsulating Requests as Objects

In the previous article, we explored the Observer Pattern, which enables event-driven communication through publishers and subscribers.

Observer answers:

How do I notify multiple interested parties when something happens?

Command answers a different question:

How do I represent an action or request as an object?

This pattern sits at the heart of:

  • Job Schedulers
  • Spring Batch
  • Quartz Scheduler
  • Workflow Engines
  • Undo/Redo Systems
  • Task Queues
  • Enterprise Integration Platforms

If you’ve ever:

  • Submitted a task for later execution
  • Scheduled a batch job
  • Built an approval workflow
  • Implemented undo functionality

you’ve likely encountered Command Pattern.


The Problem: Tight Coupling Between Sender and Receiver

Imagine a UI button.

When clicked:

Button Click
     ↓
Generate Report

A naive implementation:

public class ReportButton {

    private ReportService service =
            new ReportService();

    public void click() {

        service.generateReport();
    }
}

Looks fine.


Now requirements evolve.

The button may perform:

Generate Report
Export PDF
Send Email
Create Invoice
Run Batch Job

Suddenly:

Button
Knows Too Much

The sender becomes tightly coupled to the receiver.

This violates good design principles.


What We Really Want

Instead of:

Button
   │
   ▼
Specific Service

We want:

Button
   │
   ▼
Command
   │
   ▼
Receiver

The button shouldn’t care what happens.

It should simply execute a command.

This is Command Pattern.


What is Command Pattern?

Command is a Behavioral Design Pattern that:

Encapsulates a request as an object, allowing requests to be parameterized, queued, logged, scheduled, or undone.

Think of a restaurant.

Customer:

Place Order

Waiter:

Takes Order

Kitchen:

Prepares Food

The waiter doesn’t cook.

The kitchen doesn’t interact directly with the customer.

The order acts as the command.


Real Life Analogy

Imagine a TV remote.

Remote button:

Power On

The remote doesn’t know:

How TV boots
How circuits work
How hardware initializes

It simply sends a command.

The television executes it.

Classic Command Pattern.


Command Architecture

Client
   │
   ▼

Command

   │
   ▼

Receiver

   ▲
   │

Invoker

Components:

Client
Creates Command

Invoker
Executes Command

Receiver
Performs Work

Step 1: Create Command Interface

public interface Command {

    void execute();
}

Simple.

Every command supports execution.


Step 2: Create Receiver

public class ReportService {

    public void generateReport() {

        System.out.println(
            "Generating Report");
    }
}

Receiver contains actual business logic.


Step 3: Create Concrete Command

public class GenerateReportCommand
        implements Command {

    private ReportService service;

    public GenerateReportCommand(
            ReportService service) {

        this.service = service;
    }

    @Override
    public void execute() {

        service.generateReport();
    }
}

Notice:

Command delegates to receiver.


Step 4: Create Invoker

public class Button {

    private Command command;

    public Button(Command command) {

        this.command = command;
    }

    public void click() {

        command.execute();
    }
}

Button knows only:

Command

Not ReportService.


Step 5: Client Usage

ReportService service =
        new ReportService();

Command command =
        new GenerateReportCommand(
            service);

Button button =
        new Button(command);

button.click();

Output:

Generating Report

The button never knows what work is actually performed.


Why Command Works

Without Command:

Button
   │
   ▼
Report Service

Strong coupling.


With Command:

Button
   │
   ▼
Command
   │
   ▼
Service

Loose coupling.

Flexible execution.


Enterprise Example: Job Scheduling

Suppose a scheduler must execute:

Daily Report
Monthly Billing
User Cleanup
Inventory Sync

Without Command:

if(jobType.equals(...))

Large conditional blocks emerge.


With Command:

DailyReportCommand

BillingCommand

CleanupCommand

InventorySyncCommand

Scheduler executes:

command.execute();

regardless of job type.


Quartz Scheduler Example

Quartz internally works very similarly.

Developer creates:

public class BillingJob
        implements Job {

    public void execute(
            JobExecutionContext ctx) {

    }
}

Quartz stores:

Job Definition

and executes later.

Conceptually:

Job
=
Command

Spring Batch Example

Batch jobs contain:

Read
Process
Write

Each step behaves similarly to a command.

Framework executes tasks without knowing implementation details.


Undo Functionality

One of the classic uses of Command.

Example:

Copy
Paste
Delete
Rename

Each action becomes a command.

execute()

undo()

can be implemented.

Example:

public interface Command {

    void execute();

    void undo();
}

Now:

Execute
Undo
Redo

become possible.


Workflow Engine Example

Approval system:

Approve Loan
Reject Loan
Escalate Loan

Each action becomes a command.

Workflow engine simply executes:

command.execute();

without knowing implementation details.


Queue-Based Processing

Commands work beautifully with queues.

Architecture:

Producer
    │
    ▼
Queue
    │
    ▼
Worker

Queue stores commands.

Workers execute commands later.

This enables:

Asynchronous Processing
Retry
Scheduling
Persistence

Spring Framework Example

Many Spring features resemble Command Pattern.

Examples:

TaskExecutor

Runnable

Callable

Batch Jobs

Scheduled Tasks

Each encapsulates work as an object.


Benefits of Command Pattern

1. Loose Coupling

Invoker and receiver remain independent.


2. Queuing

Commands can be stored and executed later.


3. Scheduling

Commands work naturally with schedulers.


4. Undo/Redo Support

Classic advantage.


5. Logging and Auditing

Commands can be recorded and replayed.


6. Workflow Support

Complex processes become manageable.


Common Mistakes

Mistake 1: Command Explosion

Creating hundreds of tiny commands unnecessarily.

Use where behavior is truly independent.


Mistake 2: Business Logic in Invoker

Invoker should execute commands.

Not contain business workflows.


Mistake 3: Forgetting Receiver Separation

Command should delegate work.

Receiver should perform work.

Keep responsibilities separate.


Command vs Strategy

Common interview question.


Strategy

Represents:

Algorithm Choice

Example:

Card Payment
UPI Payment
Wallet Payment

Command

Represents:

Action
Task
Request

Example:

Generate Report
Delete User
Create Invoice

Command vs Observer


Observer

One Event
Many Listeners

Command

One Request
One Execution

Different intent.


Command vs Chain of Responsibility


Command

Encapsulates action.

Execute Task

Chain of Responsibility

Processes request through:

Handler 1
Handler 2
Handler 3

Pipeline behavior.


When Should You Use Command?

Use Command when:

✔ Requests must be encapsulated

✔ Scheduling is required

✔ Queuing is required

✔ Undo functionality exists

✔ Workflow execution exists

✔ Task execution varies

Examples:

  • Quartz Jobs
  • Spring Batch
  • Workflow Engines
  • Approval Systems
  • Task Queues
  • Scheduler Frameworks

Avoid Command when:

❌ Direct method invocation is sufficient

❌ Additional abstraction adds no value

❌ Simple systems don’t need scheduling or queuing


Behavioral Patterns Covered So Far

PatternPurpose
StrategyChoose algorithms dynamically
ObserverPublish-subscribe communication
CommandEncapsulate actions as objects

Behavioral Pattern Cheat Sheet

Strategy
→ Choose Behavior

Observer
→ Broadcast Events

Command
→ Encapsulate Actions

Final Thoughts

The Command Pattern solves a common enterprise challenge:

How do we represent actions, tasks, and requests as first-class objects?

By encapsulating requests into command objects, we gain:

  • Flexibility
  • Scheduling
  • Queuing
  • Undo Support
  • Workflow Execution
  • Better Decoupling

This is why Command appears throughout:

  • Spring Batch
  • Quartz Scheduler
  • Workflow Engines
  • Task Queues
  • Enterprise Platforms

Whenever you find yourself needing to store, schedule, queue, retry, audit, or undo actions, Command Pattern is often the right choice.

In the next article, we’ll explore another extremely important Behavioral Pattern:

Chain of Responsibility Pattern — Building Request Processing Pipelines

You’ll learn why Spring Security Filters, Servlet Filters, Validation Chains, Middleware Frameworks, and API Gateways heavily rely on Chain of Responsibility concepts.

Leave a Reply

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