0 XP · 0 scenes

Command

Turn a request into an object you can store, queue or undo
The problem

The button directly calls light.turn_on().

The idea

Wrap each action in a small object with execute() (and undo()).

Reach for it when

You need undo / redo

1

The picture from life

A waiter writes your order on a slip and hands it to the kitchen. The slip is the request in written form. It can wait in a queue, be handed to another cook, be cancelled, or be kept in a register for the bill. You can do none of this with a shout across the room.

2

The problem

What goes wrong when you do not use it.

The button directly calls light.turn_on(). You cannot undo it, queue it, log it or bind the same button to something else. The action and the thing that triggers it are glued together.

✗ Before
on_button_click()light.turn_on()# no historyLightturn_on()direct call
  • calls / uses
The button calls the light directly. Nothing remembers what happened, so no undo, queue or log.
Reach for it when
  • You need undo / redo
  • Actions should be queued, scheduled, logged or replayed
  • A button, menu and shortcut should trigger the same action
3

The idea

How the pattern fixes it.

Wrap each action in a small object with execute() (and undo()). The sender holds commands, not receivers. Keep a history stack to undo.

✓ After
Remotehistorypress(cmd)undo()«interface»Commandexecute()undo()TurnOnTurnOffLightonruns
  • implements
  • calls / uses
Each action is an object. The remote stores them, so undo is just 'take the last one back'.

Who is who

Command
execute() and undo().
TurnOn, TurnOff
Concrete commands, each holds the Light.
Light (receiver)
Does the real work.
Remote (invoker)
Runs commands and keeps the history for undo.

Build it in steps

  1. 1Define a Command interface with execute and undo.
  2. 2Write one command per action holding the receiver.
  3. 3The invoker runs a command and pushes it on a stack.
  4. 4Undo pops the stack and calls undo().
4

Watch it run

Step through the calls. Tap an arrow to jump to it.

RemoteTurnOnLight1execute()2on = True3history.append(cmd)4undo()5on = False
Step 1/5

press(TurnOn(light)): the remote runs the command object.

execute()
5

The code

Python and Java. Try writing the classes yourself from the diagram first.

class Light:
    def __init__(self):
        self.on = False


class TurnOn:
    def __init__(self, light):
        self.light = light

    def execute(self):
        self.light.on = True

    def undo(self):
        self.light.on = False


class TurnOff:
    def __init__(self, light):
        self.light = light

    def execute(self):
        self.light.on = False

    def undo(self):
        self.light.on = True


class Remote:
    def __init__(self):
        self.history = []

    def press(self, command):
        command.execute()
        self.history.append(command)

    def undo(self):
        if self.history:
            self.history.pop().undo()


def demo():
    light, remote, states = Light(), Remote(), []
    remote.press(TurnOn(light)); states.append(light.on)
    remote.press(TurnOff(light)); states.append(light.on)
    remote.undo(); states.append(light.on)
    return states

What you gain

  • Undo / redo, queues, logs and macros come naturally
  • Sender and receiver are decoupled

What it costs

  • One small class per action
  • Undo needs the command to remember enough state

You have already met it in

  • Editor undo stacks (Ctrl+Z)
  • Java's Runnable passed to an executor
  • Database transaction logs and job queues

Often mixed up with

  • StrategyStrategy swaps how something is done; Command packages a whole action to run later.
  • MementoUndo by reversing the action (Command) versus undo by restoring a saved snapshot (Memento).
6

Make it stick

Build a text editor with Type and Delete commands and an undo stack. Add redo with a second stack.

Confidence