Skip to content

Command Pattern

Definition

The Command Pattern encapsulates a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.

  • A method call becomes a first-class object: it can be stored, passed around, delayed, retried, serialized, and reversed.
  • The invoker (a button, a scheduler, a queue consumer) knows only execute(); it has no idea what the command does.

When to Use?

  • You need undo/redo, macros, or a history of actions.
  • Requests must be queued, scheduled, or sent across a boundary.
  • The thing that triggers an action should not be coupled to the thing that performs it.

Use-case Examples (Real-world Applications)

  • Editor undo stacks
  • Menu items and toolbar buttons bound to the same action
  • Job queues, task runners, transactional outboxes
  • java.lang.Runnable, the smallest possible command interface

Structure

uml diagram

Example

Three roles: the receiver (Light) knows how to do the work, the command wraps a receiver plus the arguments needed to invoke it, and the invoker only stores and triggers commands.

import java.util.ArrayDeque;
import java.util.Deque;

public class Light {
    private final String room;

    public Light(String room) {
        this.room = room;
    }

    public void on() {
        System.out.println(room + " light on");
    }

    public void off() {
        System.out.println(room + " light off");
    }
}

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

public class LightOnCommand implements Command {
    private final Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.on();
    }

    @Override
    public void undo() {
        light.off();
    }
}

public class RemoteControl {
    private final Deque<Command> history = new ArrayDeque<>();

    public void submit(Command command) {
        command.execute();
        history.push(command);
    }

    public void undoLast() {
        if (!history.isEmpty()) {
            history.pop().undo();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        RemoteControl remote = new RemoteControl();

        remote.submit(new LightOnCommand(new Light("Kitchen"))); // Kitchen light on
        remote.undoLast();                                       // Kitchen light off
    }
}
public class Light
{
    private readonly string _room;

    public Light(string room) => _room = room;

    public void On() => Console.WriteLine($"{_room} light on");
    public void Off() => Console.WriteLine($"{_room} light off");
}

public interface ICommand
{
    void Execute();
    void Undo();
}

public class LightOnCommand : ICommand
{
    private readonly Light _light;

    public LightOnCommand(Light light) => _light = light;

    public void Execute() => _light.On();
    public void Undo() => _light.Off();
}

public class RemoteControl
{
    private readonly Stack<ICommand> _history = new();

    public void Submit(ICommand command)
    {
        command.Execute();
        _history.Push(command);
    }

    public void UndoLast()
    {
        if (_history.Count > 0) _history.Pop().Undo();
    }
}

var remote = new RemoteControl();
remote.Submit(new LightOnCommand(new Light("Kitchen"))); // Kitchen light on
remote.UndoLast();                                       // Kitchen light off
#include <iostream>
#include <memory>
#include <stack>
#include <string>

class Light {
public:
    explicit Light(std::string room) : room_(std::move(room)) {}

    void on() { std::cout << room_ << " light on\n"; }
    void off() { std::cout << room_ << " light off\n"; }

private:
    std::string room_;
};

class Command {
public:
    virtual ~Command() = default;
    virtual void execute() = 0;
    virtual void undo() = 0;
};

class LightOnCommand : public Command {
public:
    explicit LightOnCommand(Light& light) : light_(light) {}

    void execute() override { light_.on(); }
    void undo() override { light_.off(); }

private:
    Light& light_;
};

class RemoteControl {
public:
    void submit(std::unique_ptr<Command> command) {
        command->execute();
        history_.push(std::move(command));
    }

    void undoLast() {
        if (history_.empty()) return;
        history_.top()->undo();
        history_.pop();
    }

private:
    std::stack<std::unique_ptr<Command>> history_;
};

int main() {
    Light kitchen("Kitchen");
    RemoteControl remote;

    remote.submit(std::make_unique<LightOnCommand>(kitchen)); // Kitchen light on
    remote.undoLast();                                        // Kitchen light off
}
from abc import ABC, abstractmethod


class Light:
    def __init__(self, room: str) -> None:
        self.room = room

    def on(self) -> None:
        print(f"{self.room} light on")

    def off(self) -> None:
        print(f"{self.room} light off")


class Command(ABC):
    @abstractmethod
    def execute(self) -> None: ...

    @abstractmethod
    def undo(self) -> None: ...


class LightOnCommand(Command):
    def __init__(self, light: Light) -> None:
        self._light = light

    def execute(self) -> None:
        self._light.on()

    def undo(self) -> None:
        self._light.off()


class RemoteControl:
    def __init__(self) -> None:
        self._history: list[Command] = []

    def submit(self, command: Command) -> None:
        command.execute()
        self._history.append(command)

    def undo_last(self) -> None:
        if self._history:
            self._history.pop().undo()


remote = RemoteControl()
remote.submit(LightOnCommand(Light("Kitchen")))  # Kitchen light on
remote.undo_last()                               # Kitchen light off
use std::cell::RefCell;
use std::rc::Rc;

struct Light {
    room: String,
}

impl Light {
    fn on(&self) {
        println!("{} light on", self.room);
    }
    fn off(&self) {
        println!("{} light off", self.room);
    }
}

trait Command {
    fn execute(&self);
    fn undo(&self);
}

struct LightOnCommand {
    light: Rc<RefCell<Light>>,
}

impl Command for LightOnCommand {
    fn execute(&self) {
        self.light.borrow().on();
    }
    fn undo(&self) {
        self.light.borrow().off();
    }
}

#[derive(Default)]
struct RemoteControl {
    history: Vec<Box<dyn Command>>,
}

impl RemoteControl {
    fn submit(&mut self, command: Box<dyn Command>) {
        command.execute();
        self.history.push(command);
    }

    fn undo_last(&mut self) {
        if let Some(command) = self.history.pop() {
            command.undo();
        }
    }
}

fn main() {
    let kitchen = Rc::new(RefCell::new(Light { room: "Kitchen".into() }));
    let mut remote = RemoteControl::default();

    remote.submit(Box::new(LightOnCommand { light: kitchen.clone() }));
    remote.undo_last();
}
class Light {
  constructor(private readonly room: string) {}

  on(): void {
    console.log(`${this.room} light on`);
  }

  off(): void {
    console.log(`${this.room} light off`);
  }
}

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

class LightOnCommand implements Command {
  constructor(private readonly light: Light) {}

  execute(): void {
    this.light.on();
  }

  undo(): void {
    this.light.off();
  }
}

class RemoteControl {
  private readonly history: Command[] = [];

  submit(command: Command): void {
    command.execute();
    this.history.push(command);
  }

  undoLast(): void {
    this.history.pop()?.undo();
  }
}

const remote = new RemoteControl();
remote.submit(new LightOnCommand(new Light("Kitchen"))); // Kitchen light on
remote.undoLast();                                       // Kitchen light off

Macro commands

Because a command is an object, a list of commands is also a command, the Composite Pattern applied to behaviour:

public record MacroCommand(java.util.List<Command> commands) implements Command {
    @Override
    public void execute() {
        commands.forEach(Command::execute);
    }

    @Override
    public void undo() {
        commands.reversed().forEach(Command::undo); // reverse order matters
    }
}

Check Your Understanding

#

What does the Command Pattern encapsulate?