Home → Design Patterns → Article

Command Pattern: Encapsulating Actions as Objects

Learn how the Command Pattern separates 'what to do' from 'how to trigger it' to create more flexible and maintainable code

1 min read
27 words

Command Pattern: Encapsulating Actions as Objects

Situation: You’re Building a UI with Buttons

You have a GUI or CLI where a user can:

  • Click “Save”
  • Click “Print”
  • Click “Undo”
  • Click “Redo”

You write something like:

if (button.equals("save")) saveDog();
else if (button.equals("print")) printDoc();
else if (button.equals("undo")) undo();

Then Requirements Start Stacking

  • Add keyboard shortcuts for each action
  • Add macros (run a sequence of commands)
  • Add undo history
  • Add dynamic remapping of buttons
  • Each action might need different parameters

Now your code is tangled. Logic is everywhere. You can’t:

  • Queue actions
  • Log what was done
  • Revert past actions
  • Run same logic later with same behavior

You’re writing “action triggers” and “execution logic” in the same place - it’s a giant ball of mud.

What’s the Problem?

  • You’re mixing what to do with how to trigger it
  • No clean way to queue, undo, or reuse actions
  • Impossible to treat actions like objects
  • No audit trail, no flexibility

How Command Pattern Saves You

Command pattern says:

“Wrap every action inside a Command object - now it can be passed around, stored, and executed when needed.”

Instead of directly calling methods, you create command classes that encapsulate an action and its parameters. Each command implements a common interface with methods like execute() and undo().

Interface

// Command interface
interface Command {
  execute(): void;
  undo(): void;
}

Concrete commands

// Concrete commands
class SaveCommand implements Command {
  private document: Document;
  
  constructor(document: Document) {
    this.document = document;
  }
  
  execute() {
    this.document.save();
  }
  
  undo() {
    // Restore previous state
  }
}

Invoker class (e.g., a button)

// Command invoker
class Button {
  private command: Command;
  
  setCommand(command: Command) {
    this.command = command;
  }
  
  click() {
    this.command.execute();
  }
}

Usage


Button saveBtn = new Button(new SaveCommand());
Button printBtn = new Button(new PrintCommand());

saveBtn.click();  // Output: Saving the document...
printBtn.click(); // Output: Printing the document...

Benefits of the Command Pattern

  • Separation of concerns: The invoker is decoupled from the receiver
  • Extensibility: Add new commands without changing existing code
  • History: Store command objects for audit logs or undo functionality
  • Queuing: Commands can be scheduled and executed later
  • Composability: Create macro commands that execute multiple commands
  • Transactional behavior: Roll back a sequence of commands if one fails

Real-World Use Cases

  • GUI actions (buttons, menu items, keyboard shortcuts)
  • Transactional systems where operations need to be rolled back
  • Remote procedure calls where commands are serialized and sent over a network
  • Task schedulers and job queues
  • Multi-level undo/redo functionality
  • Gaming input systems

When to Use It

Use the Command Pattern when you need to:

  1. Parameterize objects with operations
  2. Queue, schedule, or execute operations remotely
  3. Support undoable operations
  4. Structure a system around high-level operations
  5. Implement callback functionality in a type-safe way

By encapsulating actions as objects, the Command Pattern brings flexibility and structure to your code, making it easier to extend and maintain as requirements grow.

System Design Mastery

Ready to ace your next system design interview?

Go beyond surface-level concepts. My ebook covers 35 real-world problems with battle-tested patterns, follow-up questions interviewers actually ask, and the principles behind building systems at scale.

  • 35 comprehensive problem breakdowns
  • Proven patterns that work
  • Real follow-up questions & answers
Get the Ebook
📘
System Design
Interview Guide
35 Problems