Skip to content
On this page

Command

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

The Command pattern is about encapsulating a request as a command, then executing that command.

Before using the Command pattern, let's consider an example of a text editor to see how to implement simple editing operations:

java
public class TextEditor {
    private StringBuilder buffer = new StringBuilder();

    public void copy() {
        ...
    }

    public void paste() {
        String text = getFromClipBoard();
        add(text);
    }

    public void add(String s) {
        buffer.append(s);
    }

    public void delete() {
        if (buffer.length() > 0) {
            buffer.deleteCharAt(buffer.length() - 1);
        }
    }

    public String getState() {
        return buffer.toString();
    }
}

We use a StringBuilder to simulate a text editor that supports methods like copy(), paste(), add(), and delete().

Under normal circumstances, we would call TextEditor like this:

java
TextEditor editor = new TextEditor();
editor.add("Command pattern in text editor.\n");
editor.copy();
editor.paste();
System.out.println(editor.getState());

This directly calls methods, requiring the caller to understand all the interfaces of TextEditor.

If we switch to the Command pattern, we need to separate the sender of the command from the executor of the command. How do we separate them?

The solution is to introduce a Command interface:

java
public interface Command {
    void execute();
}

The caller creates a corresponding Command and executes it without worrying about the internal execution details.

To support CopyCommand and PasteCommand, we derive from the Command interface:

java
public class CopyCommand implements Command {
    // Hold the receiver object:
    private TextEditor receiver;

    public CopyCommand(TextEditor receiver) {
        this.receiver = receiver;
    }

    public void execute() {
        receiver.copy();
    }
}

public class PasteCommand implements Command {
    private TextEditor receiver;

    public PasteCommand(TextEditor receiver) {
        this.receiver = receiver;
    }

    public void execute() {
        receiver.paste();
    }
}

Finally, we assemble the Command and TextEditor in the client as follows:

java
TextEditor editor = new TextEditor();
editor.add("Command pattern in text editor.\n");
// Execute a CopyCommand:
Command copy = new CopyCommand(editor);
copy.execute();
editor.add("----\n");
// Execute a PasteCommand:
Command paste = new PasteCommand(editor);
paste.execute();
System.out.println(editor.getState());

This is the structure of the Command pattern:

┌──────┐      ┌───────┐
│Client│─ ─ ─▶│Command│
└──────┘      └───────┘
                  │  ┌──────────────┐
                  ├─▶│ CopyCommand  │
                  │  ├──────────────┤
                  │  │editor.copy() │─ ┐
                  │  └──────────────┘
                  │                    │  ┌────────────┐
                  │  ┌──────────────┐   ─▶│ TextEditor │
                  └─▶│ PasteCommand │  │  └────────────┘
                     ├──────────────┤
                     │editor.paste()│─ ┘
                     └──────────────┘

Some might question: Creating a bunch of Command classes adds multiple classes and isn't as simple as directly writing:

java
TextEditor editor = new TextEditor();
editor.add("Command pattern in text editor.\n");
editor.copy();
editor.paste();

Indeed, using the Command pattern increases the system's complexity. If the requirements are simple, direct method calls are more intuitive and straightforward.

So, do we still need the Command pattern?

The answer depends on the requirements. If the TextEditor becomes complex enough and needs to support Undo and Redo functionalities, the Command pattern becomes necessary because we can add an undo() method to each command:

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

Then, we can save a series of executed commands in a List, enabling support for Undo and Redo. At this point, we also need an Invoker object responsible for executing commands and saving command history:

┌─────────────┐
│   Client    │
└─────────────┘





┌─────────────┐
│   Invoker   │
├─────────────┤    ┌───────┐
│List commands│─ ─▶│Command│
│invoke(c)    │    └───────┘
│undo()       │        │  ┌──────────────┐
└─────────────┘        ├─▶│ CopyCommand  │
                       │  ├──────────────┤
                       │  │editor.copy() │─ ┐
                       │  └──────────────┘
                       │                    │  ┌────────────┐
                       │  ┌──────────────┐   ─▶│ TextEditor │
                       └─▶│ PasteCommand │  │  └────────────┘
                          ├──────────────┤
                          │editor.paste()│─ ┘
                          └──────────────┘

As shown, the pattern increases the design complexity in proportion to the requirements but reduces the coupling between different components of the system.

Exercise

Add Add and Delete commands to the Command pattern and support Undo and Redo operations.

Summary

  • The Command pattern's design philosophy is to separate the creation and execution of commands, allowing callers to execute commands without knowing the specific execution process.
  • By encapsulating Command objects, the Command pattern can save executed commands, enabling operations like undo and redo.
Command has loaded