Command Pattern explained simply
Thilan Dissanayaka Software Architecture February 08, 2020

Command Pattern explained simply

What if you could turn every action in your application into an object? Something you could store, queue up, undo, log, and replay whenever you want. That is exactly what the Command Pattern does.

What is the Command Pattern?

  • Encapsulates a request as an object, containing all the information needed to perform the action.
  • Decouples the sender of a request from the receiver that actually does the work.
  • Enables undo/redo, queuing, logging, and macro recording out of the box.

In short:

Turn actions into objects.

Real-Life Analogy

Think about how a restaurant works. You sit down, and the waiter takes your order. But the waiter doesn’t cook anything. Instead, they write your order on a slip of paper and pass it to the kitchen.

That order slip is the command. It contains everything the kitchen needs to know – what dish, how you want it cooked, any modifications. The waiter (invoker) doesn’t need to know how to cook. The kitchen (receiver) doesn’t need to interact with you directly. And that slip can be queued, prioritized, or even cancelled before the kitchen gets to it.

That separation is exactly what the Command Pattern gives you in code.

Structure

  • Command (Interface): Declares execute() and undo() methods. Every concrete command implements this.
  • InsertCommand / DeleteCommand (Concrete Commands): Each one knows exactly how to perform a specific action on the receiver, and how to reverse it.
  • CommandHistory (Invoker): Doesn’t know what the commands actually do. It just calls execute() and keeps a stack so it can call undo() later.
  • TextEditor (Receiver): The object that actually does the real work – inserting and deleting text.

The invoker never talks to the receiver directly. It only talks through command objects. That is the entire point.

Example in Java – Text Editor with Undo/Redo

Text editors are the classic example here. Every keystroke, every paste, every delete is a command. And you need to undo any of them at any time. Let’s build it.

Command Interface

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

Two methods. That is all a command needs. Do something, and know how to reverse it.

TextEditor Class (Receiver)

public class TextEditor {
    private final StringBuilder content = new StringBuilder();

    public void insert(int position, String text) {
        content.insert(position, text);
    }

    public String delete(int position, int length) {
        String deleted = content.substring(position, position + length);
        content.delete(position, position + length);
        return deleted;
    }

    public String getContent() {
        return content.toString();
    }
}

The receiver is straightforward. It inserts text, deletes text, and returns the current content. It has no idea that commands exist.

InsertCommand

public class InsertCommand implements Command {
    private final TextEditor editor;
    private final String text;
    private final int position;

    public InsertCommand(TextEditor editor, int position, String text) {
        this.editor = editor;
        this.position = position;
        this.text = text;
    }

    @Override
    public void execute() {
        editor.insert(position, text);
    }

    @Override
    public void undo() {
        editor.delete(position, text.length());
    }
}

Inserting text is easy to undo – you just delete the same number of characters from the same position.

DeleteCommand

public class DeleteCommand implements Command {
    private final TextEditor editor;
    private final int position;
    private final int length;
    private String deletedText;

    public DeleteCommand(TextEditor editor, int position, int length) {
        this.editor = editor;
        this.position = position;
        this.length = length;
    }

    @Override
    public void execute() {
        deletedText = editor.delete(position, length);
    }

    @Override
    public void undo() {
        editor.insert(position, deletedText);
    }
}

This is where it gets interesting. The DeleteCommand saves the deleted text during execute() so it can restore it on undo(). The command object carries its own state.

CommandHistory (Invoker)

import java.util.Stack;

public class CommandHistory {
    private final Stack<Command> history = new Stack<>();

    public void executeCommand(Command cmd) {
        cmd.execute();
        history.push(cmd);
    }

    public void undo() {
        if (!history.isEmpty()) {
            Command cmd = history.pop();
            cmd.undo();
        }
    }
}

The invoker has no clue whether it is running an insert, a delete, or anything else. It just calls execute(), pushes to the stack, and pops when you want to undo. Dead simple.

Putting It All Together

public class MainProgram {
    public static void main(String[] args) {
        TextEditor editor = new TextEditor();
        CommandHistory history = new CommandHistory();

        // insert "Hello"
        history.executeCommand(new InsertCommand(editor, 0, "Hello"));
        System.out.println(editor.getContent());  // Hello

        // insert " World"
        history.executeCommand(new InsertCommand(editor, 5, " World"));
        System.out.println(editor.getContent());  // Hello World

        // delete "World"
        history.executeCommand(new DeleteCommand(editor, 6, 5));
        System.out.println(editor.getContent());  // Hello

        // undo the delete
        history.undo();
        System.out.println(editor.getContent());  // Hello World

        // undo the second insert
        history.undo();
        System.out.println(editor.getContent());  // Hello
    }
}

Output:

Hello
Hello World
Hello
Hello World
Hello

Each action is an object. Each object knows how to do and undo itself. The history stack just orchestrates the sequence. You could extend this with redo by adding a second stack, and the pattern stays clean.

Key Components

Component Role In Our Example
Command Declares the execution interface Command interface
Concrete Command Implements a specific action and its reverse InsertCommand, DeleteCommand
Invoker Asks the command to carry out the request CommandHistory
Receiver Knows how to perform the actual work TextEditor

When to Use the Command Pattern

  • You need undo/redo functionality.
  • You want to queue operations and execute them later.
  • You need to log operations for auditing or replay.
  • You want to support macro recording – a sequence of commands bundled together.
  • You need to decouple the object that invokes an operation from the one that performs it.

Advantages

  • Undo/Redo becomes trivial – each command knows how to reverse itself.
  • Loose coupling – the invoker and receiver don’t know about each other.
  • Composability – you can combine simple commands into macro commands.
  • Extensibility – adding a new command means writing one class. No changes to existing code.

Disadvantages

  • Class explosion – every distinct action needs its own command class.
  • Complexity – for simple operations, wrapping everything in command objects can feel like overkill.
  • State management – commands that need to store state for undo can get tricky in complex scenarios.

Real-World Use Cases

  • Java Runnable: The Runnable interface is essentially the Command Pattern. You pass an action as an object to a thread pool.
  • Transaction Systems: Database transactions wrap operations as commands that can be committed or rolled back.
  • Task Queues: Job queues in systems like Celery or Sidekiq treat each job as a serialized command object.
  • Macro Recording: Applications like Photoshop or Excel record sequences of commands and replay them.
  • Git: Every git commit, git revert, git cherry-pick is a command object that can be composed, reordered, and undone.
  • GUI Frameworks: Menu items, toolbar buttons, and keyboard shortcuts all map to command objects – which is why the same action works from three different places.

Wrapping Up

The Command Pattern turns actions into first-class objects. Once you do that, you unlock undo, redo, queuing, logging, and macro recording almost for free. The text editor example shows the core idea – every edit is an object that knows how to execute and reverse itself, and the history stack handles the rest.

If you ever find yourself needing to track, queue, or reverse operations in your application, the Command Pattern is exactly what you are looking for.

ALSO READ
Writing a Shell Code for Linux
Apr 21, 2020 Exploit Development

Shellcode is a small piece of machine code used as the payload in exploit development. In this post, we write Linux shellcode from scratch — starting with a simple exit, building up to spawning a shell, and explaining every decision along the way.

Exploiting a Stack Buffer Overflow on Windows
Apr 12, 2020 Exploit Development

In a previous tutorial we discusses how we can exploit a buffer overflow vulnerability on a Linux machine. I wen through all theories in depth and explained each step. Now today we are going to jump...

Exploiting a  Stack Buffer Overflow  on Linux
Apr 01, 2020 Exploit Development

Have you ever wondered how attackers gain control over remote servers? How do they just run some exploit and compromise a computer? If we dive into the actual context, there is no magic happening....

Basic concepts of Cryptography
Mar 01, 2020 Cryptography

Ever notice that little padlock icon in your browser's address bar? That's cryptography working silently in the background, protecting everything you do online. Whether you're sending an email,...

Common Web Application Attacks
Feb 05, 2020 Application Security

Web applications are one of the most targeted surfaces by attackers. This is primarily because they are accessible over the internet, making them exposed and potentially vulnerable. Since these...

Remote Code Execution (RCE)
Jan 02, 2020 Application Security

Remote Code Execution (RCE) is the holy grail of application security vulnerabilities. It allows an attacker to execute arbitrary code on a remote server — and the consequences are as bad as it sounds. In this post, we'll go deep into RCE across multiple languages, including PHP, Java, Python, and Node.js.

> > >