← Back to All Design Patterns

Command Pattern

🎯 Explain Like I'm 5...

Imagine you're at a restaurant and you want to order food! 🍕🍔

😟 Without Commands:

  • You tell the waiter what you want
  • The waiter has to remember everything in their head
  • Hard to change orders, no way to undo!
  • Kitchen doesn't have a record of what to make

💡 The Solution - Use Order Slips (Commands)!

  • You tell the waiter your order 📝
  • Waiter writes it on a paper slip (the COMMAND!)
  • The slip goes to the kitchen
  • Kitchen reads the slip and makes your food
  • The slip can be saved, copied, or even undone! 🎉

🌟 The Key Idea:

A command is like a REQUEST written on paper! You can pass it around, save it, queue it up, and even UNDO it later! The order slip doesn't make the food - it just tells the kitchen WHAT to make! 📋

🚀 Why Is This Pattern Useful?

  • Undo/Redo operations - Save commands and reverse them!
  • Queue operations - Save commands and execute them later!
  • Log operations - Keep track of what happened!
  • Macro commands - Combine multiple commands into one!

📋 Pattern Purpose

The Command pattern encapsulates a REQUEST as an OBJECT, allowing you to parameterize clients with different requests, queue or log requests, and support UNDOABLE operations. It decouples the object that invokes the operation from the one that knows how to perform it.

⚡ When to Use Command Pattern

  • You need to support undo/redo operations
  • You want to queue, schedule, or log operations
  • You need to parameterize objects with operations
  • You want to support macro commands (combining multiple operations)
  • You need to decouple the object making requests from the object handling them

🧩 Command Pattern Components

  • 📦Command: Interface declaring execute() and undo() methods
  • 📦ConcreteCommand: Implements Command, binds Receiver with an action
  • 📦Receiver: The object that performs the actual work
  • 📦Invoker: Asks the command to execute the request
  • 📦Client: Creates commands and sets their receiver

💻 Java Implementations

Example 1: Remote Control (TV Commands)

A universal remote control that can execute and undo various TV operations.

Command.java
java
1
2
3
4
5
// Command Interface
public interface Command {
void execute();
void undo();
}
TV.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Receiver - The object that performs the actual work
public class TV {
private boolean isOn = false;
private int volume = 0;
public void on() {
isOn = true;
System.out.println("TV is ON");
}
public void off() {
isOn = false;
System.out.println("TV is OFF");
}
public void volumeUp() {
if (isOn) {
volume++;
System.out.println("TV volume: " + volume);
}
}
public void volumeDown() {
if (isOn && volume > 0) {
volume--;
System.out.println("TV volume: " + volume);
}
}
public boolean isOn() {
return isOn;
}
public int getVolume() {
return volume;
}
}
TVOnCommand.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Concrete Command - Turn TV On
public class TVOnCommand implements Command {
private TV tv;
public TVOnCommand(TV tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.on();
}
@Override
public void undo() {
tv.off();
}
}
TVOffCommand.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Concrete Command - Turn TV Off
public class TVOffCommand implements Command {
private TV tv;
public TVOffCommand(TV tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.off();
}
@Override
public void undo() {
tv.on();
}
}
VolumeUpCommand.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Concrete Command - Volume Up
public class VolumeUpCommand implements Command {
private TV tv;
public VolumeUpCommand(TV tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.volumeUp();
}
@Override
public void undo() {
tv.volumeDown();
}
}
RemoteControl.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// Invoker - Executes commands
public class RemoteControl {
private Command command;
private Command lastCommand;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
lastCommand = command;
}
public void pressUndo() {
if (lastCommand != null) {
lastCommand.undo();
}
}
}
// Client code
public class RemoteDemo {
public static void main(String[] args) {
// Receiver
TV tv = new TV();
// Commands
Command tvOn = new TVOnCommand(tv);
Command tvOff = new TVOffCommand(tv);
Command volUp = new VolumeUpCommand(tv);
// Invoker
RemoteControl remote = new RemoteControl();
// Turn on TV
remote.setCommand(tvOn);
remote.pressButton();
// Increase volume
remote.setCommand(volUp);
remote.pressButton();
remote.pressButton();
// Undo last command
remote.pressUndo();
// Turn off TV
remote.setCommand(tvOff);
remote.pressButton();
// Undo - turns TV back on!
remote.pressUndo();
}
}

Example 2: Text Editor (Copy, Paste, Undo)

Text editor commands that support undo functionality.

TextEditor.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Receiver - The document being edited
public class TextEditor {
private StringBuilder text = new StringBuilder();
private String clipboard = "";
public void write(String content) {
text.append(content);
System.out.println("Text: " + text);
}
public void delete(int length) {
if (text.length() >= length) {
text.delete(text.length() - length, text.length());
System.out.println("Text: " + text);
}
}
public void copy(int length) {
if (text.length() >= length) {
clipboard = text.substring(text.length() - length);
System.out.println("Copied: " + clipboard);
}
}
public void paste() {
text.append(clipboard);
System.out.println("Text: " + text);
}
public String getText() {
return text.toString();
}
public String getClipboard() {
return clipboard;
}
}
WriteCommand.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Concrete Command - Write text
public class WriteCommand implements Command {
private TextEditor editor;
private String content;
public WriteCommand(TextEditor editor, String content) {
this.editor = editor;
this.content = content;
}
@Override
public void execute() {
editor.write(content);
}
@Override
public void undo() {
editor.delete(content.length());
}
}
CopyCommand.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Concrete Command - Copy text
public class CopyCommand implements Command {
private TextEditor editor;
private int length;
private String previousClipboard;
public CopyCommand(TextEditor editor, int length) {
this.editor = editor;
this.length = length;
}
@Override
public void execute() {
previousClipboard = editor.getClipboard();
editor.copy(length);
}
@Override
public void undo() {
// Restore previous clipboard (simplified)
System.out.println("Copy undone - clipboard restored");
}
}
PasteCommand.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Concrete Command - Paste text
public class PasteCommand implements Command {
private TextEditor editor;
private int pastedLength;
public PasteCommand(TextEditor editor) {
this.editor = editor;
}
@Override
public void execute() {
String clipboard = editor.getClipboard();
pastedLength = clipboard.length();
editor.paste();
}
@Override
public void undo() {
editor.delete(pastedLength);
}
}
EditorDemo.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Editor with undo support
import java.util.Stack;
public class EditorDemo {
private TextEditor editor = new TextEditor();
private Stack<Command> history = new Stack<>();
public void executeCommand(Command command) {
command.execute();
history.push(command);
}
public void undo() {
if (!history.isEmpty()) {
Command command = history.pop();
command.undo();
} else {
System.out.println("Nothing to undo!");
}
}
public static void main(String[] args) {
EditorDemo demo = new EditorDemo();
// Write "Hello "
demo.executeCommand(new WriteCommand(demo.editor, "Hello "));
// Write "World!"
demo.executeCommand(new WriteCommand(demo.editor, "World!"));
// Copy last 6 characters ("World!")
demo.executeCommand(new CopyCommand(demo.editor, 6));
// Paste
demo.executeCommand(new PasteCommand(demo.editor));
// Undo paste
System.out.println("\n--- UNDO ---");
demo.undo();
// Undo copy
System.out.println("\n--- UNDO ---");
demo.undo();
// Undo write
System.out.println("\n--- UNDO ---");
demo.undo();
}
}

Example 3: Smart Home System

Smart home automation with undoable light commands.

Light.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Receiver - Smart Light
public class Light {
private String location;
private boolean isOn = false;
private int brightness = 0; // 0-100
public Light(String location) {
this.location = location;
}
public void on() {
isOn = true;
brightness = 100;
System.out.println(location + " light is ON (100% brightness)");
}
public void off() {
isOn = false;
brightness = 0;
System.out.println(location + " light is OFF");
}
public void dim(int level) {
if (isOn && level >= 0 && level <= 100) {
brightness = level;
System.out.println(location + " light dimmed to " + level + "%");
}
}
public boolean isOn() {
return isOn;
}
public int getBrightness() {
return brightness;
}
}
LightOnCommand.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Concrete Command - Turn light on
public class LightOnCommand implements Command {
private Light light;
private int previousBrightness;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
previousBrightness = light.getBrightness();
light.on();
}
@Override
public void undo() {
if (previousBrightness == 0) {
light.off();
} else {
light.dim(previousBrightness);
}
}
}
LightOffCommand.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Concrete Command - Turn light off
public class LightOffCommand implements Command {
private Light light;
private int previousBrightness;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
previousBrightness = light.getBrightness();
light.off();
}
@Override
public void undo() {
if (previousBrightness > 0) {
light.on();
light.dim(previousBrightness);
}
}
}
DimLightCommand.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Concrete Command - Dim light
public class DimLightCommand implements Command {
private Light light;
private int level;
private int previousBrightness;
public DimLightCommand(Light light, int level) {
this.light = light;
this.level = level;
}
@Override
public void execute() {
previousBrightness = light.getBrightness();
if (!light.isOn()) {
light.on();
}
light.dim(level);
}
@Override
public void undo() {
if (previousBrightness == 0) {
light.off();
} else {
light.dim(previousBrightness);
}
}
}
MacroCommand.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Macro Command - Execute multiple commands
import java.util.ArrayList;
import java.util.List;
public class MacroCommand implements Command {
private List<Command> commands;
public MacroCommand(List<Command> commands) {
this.commands = new ArrayList<>(commands);
}
@Override
public void execute() {
System.out.println("--- Executing Macro Command ---");
for (Command command : commands) {
command.execute();
}
}
@Override
public void undo() {
System.out.println("--- Undoing Macro Command ---");
// Undo in reverse order
for (int i = commands.size() - 1; i >= 0; i--) {
commands.get(i).undo();
}
}
}
SmartHomeDemo.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Smart Home Demo with Macro Commands
import java.util.Arrays;
public class SmartHomeDemo {
public static void main(String[] args) {
// Receivers
Light livingRoom = new Light("Living Room");
Light bedroom = new Light("Bedroom");
Light kitchen = new Light("Kitchen");
// Commands
Command livingRoomOn = new LightOnCommand(livingRoom);
Command bedroomOn = new LightOnCommand(bedroom);
Command kitchenOn = new LightOnCommand(kitchen);
Command livingRoomOff = new LightOffCommand(livingRoom);
Command bedroomDim = new DimLightCommand(bedroom, 30);
// Macro: "Good Night" - Dim bedroom, turn off others
MacroCommand goodNight = new MacroCommand(Arrays.asList(
bedroomDim,
livingRoomOff,
kitchenOn // Night light in kitchen
));
// Macro: "All On"
MacroCommand allOn = new MacroCommand(Arrays.asList(
livingRoomOn,
bedroomOn,
kitchenOn
));
// Execute macro commands
System.out.println("=== ALL ON ===");
allOn.execute();
System.out.println("\n=== GOOD NIGHT MODE ===");
goodNight.execute();
System.out.println("\n=== UNDO GOOD NIGHT ===");
goodNight.undo();
System.out.println("\n=== UNDO ALL ON ===");
allOn.undo();
}
}

🌍 Real-World Examples

  • 📝Text Editors: Every edit operation is a command with undo
  • 🖱️GUI Buttons: Each button action is a command object
  • 💳Transaction Systems: Commands represent transactions that can be rolled back
  • Task Schedulers: Commands queued for later execution
  • 🎬Macro Recording: Recording and replaying sequences of commands

✅ Benefits

  • Undo/Redo: Easy to implement reversible operations
  • Decoupling: Separates requester from executor
  • Extensibility: Easy to add new commands without changing existing code
  • Composite Commands: Can combine commands into macro commands
  • Logging: Commands can be logged and replayed

⚠️ Drawbacks

  • ⚠️Complexity: Increases number of classes in the system
  • ⚠️Memory: Storing command history can consume memory
  • ⚠️Overhead: May be overkill for simple operations

🔑 Key Points to Remember

  • 1️⃣Command encapsulates a REQUEST as an OBJECT
  • 2️⃣Supports undo/redo by storing state or reverse operations
  • 3️⃣Invoker doesn't know about the receiver - complete decoupling
  • 4️⃣Commands can be queued, logged, and scheduled
  • 5️⃣Macro commands combine multiple commands into one

💪 Practice Scenarios

  • Build a drawing application with undo/redo for shapes
  • Create a task queue system that executes commands in order
  • Implement a game with save/replay functionality using commands
  • Design a home automation system with scheduled commands
  • Build a calculator with operation history and undo