Skip to content

Command Design Pattern

Video Lecture

Section Video Links
Command Overview Command Overview Command Overview Command Overview 
Command Use Case Command Use Case Command Use Case Command Use Case 
Single Leading Underscore Single Leading Underscore Single Leading Underscore Single Leading Underscore 

Overview

The Command pattern is a behavioral design pattern, in which an abstraction exists between an object that invokes a command, and the object that performs it.

E.g., a button will call the Invoker, that will call a pre-registered Command, that the Receiver will perform.

A Concrete Class will delegate a request to a command object, instead of implementing the request directly.

The command pattern is a good solution for implementing UNDO/REDO functionality into your application.

Uses:

  • GUI Buttons, menus
  • Macro recording
  • Multi-level undo/redo
  • Networking - send whole command objects across a network, even as a batch
  • Parallel processing or thread pools
  • Transactional behavior
  • Wizards

Terminology

  • Receiver: The object that will receive and execute the command.
  • Invoker: The object that sends the command to the receiver. E.g., A button.
  • Command Object: Itself, an object, that implements an execute, or action method, and contains all required information to execute it.
  • Client: The application or component that is aware of the Receiver, Invoker and Commands.

Command Pattern UML Diagram

The Command Pattern UML Diagram

Source Code

...Refer to Book or Videos for extra content.

./command/command_concept.py

 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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
"The Command Pattern Concept"
from abc import ABCMeta, abstractmethod

class ICommand(metaclass=ABCMeta):  # pylint: disable=too-few-public-methods
    "The command interface, that all commands will implement"
    @staticmethod
    @abstractmethod
    def execute():
        "The required execute method that all command objects will use"

class Invoker:
    "The Invoker Class"

    def __init__(self):
        self._commands = {}

    def register(self, command_name, command):
        "Register commands in the Invoker"
        self._commands[command_name] = command

    def execute(self, command_name):
        "Execute any registered commands"
        if command_name in self._commands.keys():
            self._commands[command_name].execute()
        else:
            print(f"Command [{command_name}] not recognised")

class Receiver:
    "The Receiver"

    @staticmethod
    def run_command_1():
        "A set of instructions to run"
        print("Executing Command 1")

    @staticmethod
    def run_command_2():
        "A set of instructions to run"
        print("Executing Command 2")

class Command1(ICommand):  # pylint: disable=too-few-public-methods
    """A Command object, that implements the ICommand interface and
    runs the command on the designated receiver"""

    def __init__(self, receiver):
        self._receiver = receiver

    def execute(self):
        self._receiver.run_command_1()

class Command2(ICommand):  # pylint: disable=too-few-public-methods
    """A Command object, that implements the ICommand interface and
    runs the command on the designated receiver"""

    def __init__(self, receiver):
        self._receiver = receiver

    def execute(self):
        self._receiver.run_command_2()

# The CLient
# Create a receiver
RECEIVER = Receiver()

# Create Commands
COMMAND1 = Command1(RECEIVER)
COMMAND2 = Command2(RECEIVER)

# Register the commands with the invoker
INVOKER = Invoker()
INVOKER.register("1", COMMAND1)
INVOKER.register("2", COMMAND2)

# Execute the commands that are registered on the Invoker
INVOKER.execute("1")
INVOKER.execute("2")
INVOKER.execute("1")
INVOKER.execute("2")

Output

1
2
3
4
5
python ./command/command_concept.py
Executing Command 1
Executing Command 2
Executing Command 1
Executing Command 2

Example Use Case

...Refer to Book or Videos for extra content.

Example UML Diagram

The Command Pattern UML Diagram

Source Code

./command/client.py

 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
"The Command Pattern Use Case Example. A smart light Switch"
from light import Light
from switch import Switch
from switch_on_command import SwitchOnCommand
from switch_off_command import SwitchOffCommand

# Create a receiver
LIGHT = Light()

# Create Commands
SWITCH_ON = SwitchOnCommand(LIGHT)
SWITCH_OFF = SwitchOffCommand(LIGHT)

# Register the commands with the invoker
SWITCH = Switch()
SWITCH.register("ON", SWITCH_ON)
SWITCH.register("OFF", SWITCH_OFF)

# Execute the commands that are registered on the Invoker
SWITCH.execute("ON")
SWITCH.execute("OFF")
SWITCH.execute("ON")
SWITCH.execute("OFF")

# show history
SWITCH.show_history()

# replay last two executed commands
SWITCH.replay_last(2)

./command/light.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
"The Light. The Receiver"

class Light:
    "The Receiver"

    @staticmethod
    def turn_on():
        "A set of instructions to run"
        print("Light turned ON")

    @staticmethod
    def turn_off():
        "A set of instructions to run"
        print("Light turned OFF")

./command/switch.py

 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
"""
The Switch (Invoker) Class.
You can flick the switch and it then invokes a registered command
"""
from datetime import datetime
import time

class Switch:
    "The Invoker Class."

    def __init__(self):
        self._commands = {}
        self._history = []

    def show_history(self):
        "Print the history of each time a command was invoked"
        for row in self._history:
            print(
                f"{datetime.fromtimestamp(row[0]).strftime('%H:%M:%S')}"
                f" : {row[1]}"
            )

    def register(self, command_name, command):
        "Register commands in the Invoker"
        self._commands[command_name] = command

    def execute(self, command_name):
        "Execute any registered commands"
        if command_name in self._commands.keys():
            self._commands[command_name].execute()
            self._history.append((time.time(), command_name))
        else:
            print(f"Command [{command_name}] not recognised")

    def replay_last(self, number_of_commands):
        "Replay the last N commands"
        commands = self._history[-number_of_commands:]
        for command in commands:
            self._commands[command[1]].execute()
            #or if you want to record these replays in history
            #self.execute(command[1])

./command/switch_on_command.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
"""
A Command object, that implements the ISwitch interface and runs the
command on the designated receiver
"""
from interface_switch import ISwitch

class SwitchOnCommand(ISwitch):  # pylint: disable=too-few-public-methods
    "Switch On Command"

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

    def execute(self):
        self._light.turn_on()

./command/switch_off_command.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
"""
A Command object, that implements the ISwitch interface and runs the
command on the designated receiver
"""
from interface_switch import ISwitch

class SwitchOffCommand(ISwitch):  # pylint: disable=too-few-public-methods
    "Switch Off Command"

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

    def execute(self):
        self._light.turn_off()

./command/interface_switch.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
"The switch interface, that all commands will implement"
from abc import ABCMeta, abstractmethod

class ISwitch(metaclass=ABCMeta):  # pylint: disable=too-few-public-methods
    "The switch interface, that all commands will implement"

    @staticmethod
    @abstractmethod
    def execute():
        "The required execute method that all command objects will use"

Output

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
python ./command/client.py
Light turned ON
Light turned OFF
Light turned ON
Light turned OFF
11:23:35 : ON
11:23:35 : OFF
11:23:35 : ON
11:23:35 : OFF
Light turned ON
Light turned OFF

New Coding Concepts

_Single Leading Underscore

The single leading underscore _variable, on your class variables is a useful indicator to other developers that this property should be considered private.

Private, in C style languages, means that the variable/field/property is hidden and cannot be accessed outside of the class. It can only be used internally by its own class methods.

Python does not have a public/private accessor concept so the variable is not actually private and can still be used outside of the class in other modules.

It is just a useful construct that you will see developers use as a recommendation not to reference this variable directly outside of this class, but use a dedicated method or property instead.

Summary

...Refer to Book or Videos for extra content.