7.6. Command¶
EN: Command
PL: Polecenie
Type: object
7.6.1. Use Cases¶
GUI Buttons, menus
Macro recording
Multi level undo/redo (See Tutorial)
Networking — send whole command objects across a network, even as a batch
Parallel processing or thread pools
Transactional behaviour — Rollback whole set of commands, or defer till later
Wizards
Source 1
7.6.2. Problem¶
class Button:
__label: str
def set_label(self, name):
self.__label = name
def get_label(self):
return self.__label
def click(self):
...
if __name__ == '__main__':
button = Button()
button.set_label('My Button')
button.click()
7.6.3. Design¶
Receiver — The Object that will receive and execute the command
Invoker — Which will send the command to the receiver
Command Object — Itself, which implements an execute, or action method, and contains all required information
Client — The main application or module which is aware of the Receiver, Invoker and Commands

7.6.4. Implementation¶

Command pattern:
from abc import ABCMeta, abstractmethod
from dataclasses import dataclass
class Command(metaclass=ABCMeta):
@abstractmethod
def execute(self) -> None:
pass
class Button:
__label: str
__command: Command
def __init__(self, command: Command):
self.__command = command
def set_label(self, name):
self.__label = name
def get_label(self):
return self.__label
def click(self):
self.__command.execute()
class CustomerService:
def add_customer(self) -> None:
print('Add customer')
@dataclass
class AddCustomerCommand(Command):
__service: CustomerService
def execute(self) -> None:
self.__service.add_customer()
if __name__ == '__main__':
service = CustomerService()
command = AddCustomerCommand(service)
button = Button(command)
button.click()
# Add customer
Composite commands (Macros):
from abc import ABCMeta, abstractmethod
from dataclasses import dataclass, field
class Command(metaclass=ABCMeta):
@abstractmethod
def execute(self) -> None:
pass
class ResizeCommand(Command):
def execute(self) -> None:
print('Resize')
class BlackAndWhiteCommand(Command):
def execute(self) -> None:
print('Black And White')
@dataclass
class CompositeCommand(Command):
__commands: list[Command] = field(default_factory=list)
def add(self, command: Command) -> None:
self.__commands.append(command)
def execute(self) -> None:
for command in self.__commands:
command.execute()
if __name__ == '__main__':
composite = CompositeCommand()
composite.add(ResizeCommand())
composite.add(BlackAndWhiteCommand())
composite.execute()
# Resize
# Black And White
Undoable commands:
from abc import ABCMeta, abstractmethod
from dataclasses import dataclass, field
class Command(metaclass=ABCMeta):
@abstractmethod
def execute(self) -> None:
pass
class UndoableCommand(Command):
@abstractmethod
def unexecute(self) -> None:
pass
@dataclass
class History:
__commands: list[UndoableCommand] = field(default_factory=list)
def push(self, command: UndoableCommand) -> None:
self.__commands.append(command)
def pop(self):
return self.__commands.pop()
def size(self) -> int:
return len(self.__commands)
@dataclass
class HtmlDocument:
__content: str = ''
def set_content(self, content):
self.__content = content
def get_content(self):
return self.__content
@dataclass
class BoldCommand(UndoableCommand):
__document: HtmlDocument
__history: History = History()
__previous_content: str | None = None
def unexecute(self) -> None:
self.__document.set_content(self.__previous_content)
def apply(self, content):
return f'<b>{content}</b>'
def execute(self) -> None:
current_content = self.__document.get_content()
self.__previous_content = current_content
self.__document.set_content(self.apply(current_content))
self.__history.push(self)
@dataclass
class UndoCommand(Command):
__history: History
def execute(self) -> None:
if self.__history.size() > 0:
self.__history.pop().unexecute()
if __name__ == '__main__':
history = History()
document = HtmlDocument('Hello World')
# This should be onButtonClick or KeyboardShortcut
BoldCommand(document, history).execute()
print(document.get_content())
# <b>Hello World</b>
# This should be onButtonClick or KeyboardShortcut
UndoCommand(history).execute()
print(document.get_content())
# Hello World