Command design pattern

Command is another design pattern that can be simplified by using functions that are passed as arguments:

title UML class diagram for the command design pattern

together {
    abstract class  Caller
    abstract class  Command {
        {method}    execute()
    }
}

together {
    abstract class  Client
    abstract class  Receiver {
        {method}    action()
    }
    class           ConcreteCommand {
        state
        {method}    execute()
    }
}

Caller *-> Command
Client -> Receiver
Client -> ConcreteCommand
Receiver <- ConcreteCommand
ConcreteCommand -u-|> Command

The aim of the command design pattern is to decouple an object that calls an operation from the Receiver object that implements the operation. In the example from the design pattern book, each Caller object is a menu item in a graphical application, and the Receiver objects are the document to be edited or the application itself. For this purpose, a Command object is placed between the two, which implements an interface with a single method that calls a method in the Receiver to perform the desired operation. In this way, the Caller object does not need to know the interface of the Receiver, and different Receivers can be customised using different Command subclasses. Caller is configured with a ConcreteCommand command and calls its execute() method to execute it.

Commands are an object-orientated replacement for callbacks. [1]

The question now is whether we really need such an object-oriented replacement for callbacks in Python? Can’t we just give the Caller a function instead? So instead of calling Command.execute(), the caller could simply call command(). Command can be a class that implements __call__() and instances of Command would be callables, each containing a list of functions for future calls, for example:

1class MacroCommand:
2    """A command that executes a list of command."""
3
4    def __init__(self, commands):
5        self.commands = list(commands)
6
7    def __call__(self):
8        for command in self.commands:
9            command()
Lines 4–5

creates a list from the command arguments and ensures that it is iterable.

Line 7–8

creates a local copy of the command references in each MacroCommand instance. When an instance of MacroCommand is called, each command in self.commands is called in turn.