Command Pattern

Issue

  • The invoker (i.e. a button of a remote control) is supposed to be linked to various actions being executed when invoked

  • The amount and behaviour of these actions are open to change and must therefore be configurable

  • As a plus, the executed action(s) should be reversible

For example, the remote control’s “On” button is supposed to turn a TV on, but also turn it off, if it is already running.

Naming

Client:

class that requests a command being executed

Receiver:

class which actually performs the requested actions behind a command

Invoker:

class that maps all possible command slots (i.e. button of a remote) to a certain command’s execute() method

Command:

interface class that defines a structure of any concrete command class

ConcreteCommand:

implementation of Command interface that calls certain functions on the Receiver upon being executed

Solution

Definition

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

        classDiagram
    Command "0..*" <-- Invoker : HAS-A
    Command<|--ConcreteCommand : implements
    Receiver<--ConcreteCommand : HAS-A
    class Invoker {
        +set_command(Command)
    }
    class Command {
        <<interface>>
        +execute()
        +undo()
    }
    class Receiver {
        +action_1()
        +action_2()
    }
    class ConcreteCommand {
        +execute()
        +undo()
    }
    
  • The Receiver contains all possible action methods (e.g. action_1(), action_2(), …) -> these are depending on the actual nature of the Receiver (i.e. a light might have an on() and off() method)

  • The Invoker has a Command interface instance for every “slot” that requires a Command being executed upon activation (i.e. a four button remote requires four Command instances)

  • The Invoker defines a method for calling each “slot”-Command’s execute() method (i.e. light_on_button_pressed())

  • The Command interface defines an execute() method and should also contain an undo() method to revert an executed command’s action

  • Any ConcreteCommand class contains a Receiver instance, which the execute() and undo() methods act upon

  • The Client is the one actually using the pattern:

    • it instantiates an Invoker (“You have access to the methods I want to execute”) -> HAS-A

    • it instantiates a Receiver (“You are the one I actually want to do something!”) -> HAS-A

Routine:
  • The Client instantiates the Invoker, thereby passing the ConcreteCommand ‘s as arguments, which themselves take a receiver as an argument

  • The Client calls the Invoker’s method, that executes the corresponding command’s execute() method

Advantages:

  • Commands can execute other commands instead of directly acting on the Receiver (Macro-Commands like turning on multiple lights)

  • Undoing commands is easy to do -> enable the user to undo previous commands is often a smart way to structure and application, instead of just executing things

Example

Find a template example for Python at https://refactoring.guru/design-patterns/command/python/example

Hint

As an alternative to using “Command setter” methods in the Invoker class, you may pass the ConcreteCommand instances into the Invoker’s constructor and assign it right away.

It might be safer, not being able to reassign the commands in the invoker during runtime, but instead create multiple Invoker classes, which all assign commands differently. It also ensures, that Invokers cannot be instantiated without specifying all required commands.

from abc import ABC, abstractmethod


# Receiver
class FancyLight:

    def turn_on(self):
        print("Switching on ...")
        return

    def turn_off(self):
        print("Switching off ...")
        return


class Command(ABC):

    @abstractmethod
    def execute(self) -> None:
        pass

    @abstractmethod
    def unexecute(self) -> None:
        pass


# Concrete Commands
class TurnOnCommand(Command):

    def __init__(self, light: FancyLight):
        self.light = light

    def execute(self) -> None:
        self.light.turn_on()

    def unexecute(self) -> None:
        self.light.turn_off()


class TurnOffCommand(Command):

    def __init__(self, light: FancyLight):
        self.light = light

    def execute(self) -> None:
        self.light.turn_off()

    def unexecute(self) -> None:
        self.light.turn_on()


# Invoker
class SuperRemoteControl:
    _turn_on = None
    _turn_off = None

    _light_state = None
    _last_command = None

    def __init__(self, turn_on_command, turn_off_command):
        self._light_state = "off"
        self._turn_on = turn_on_command
        self._turn_off = turn_off_command
        self._last_command = None

    # Not needed as directly assigned in constructor (dependency injection)
    def set_turn_on(self, command: Command):
        self._turn_on = command

    # Not needed as directly assigned in constructor (dependency injection)
    def set_turn_off(self, command: Command):
        self._turn_off = command

    def press_onoff(self):
        if self._light_state == "off":
            self._turn_on.execute()
            self._light_state = "on"
            self._last_command = self._turn_on
        elif self._light_state == "on":
            self._turn_off.execute()
            self._light_state = "off"
            self._last_command = self._turn_off

    def press_undo(self):
        self._last_command.unexecute()


if __name__ == "__main__":
    light = FancyLight()
    control = SuperRemoteControl(
        turn_on_command=TurnOnCommand(light),
        turn_off_command=TurnOffCommand(light)
    )
    # Alternatively to passing commands into the constructor, they can be
    # assigned in set-methods

    # control.set_turn_on(TurnOnCommand(light))
    # control.set_turn_off(TurnOffCommand(light))
    control.press_onoff()
    control.press_onoff()
    control.press_undo()