Python-Kommandomuster
Das Befehlsmuster ist ein Verhaltens-Entwurfsmuster, das eine Anfrage als Objekt kapselt und es dadurch ermöglicht, Clients mit unterschiedlichen Anfragen zu parametrisieren. Einfach ausgedrückt,verpackt das Befehlsmuster Operationen (wie das Öffnen von Dateien, das Speichern von Daten) in unabhängige Objekte,wodurch Sie Operationen auf dieselbe Weise behandeln können, wie Sie Daten behandeln.
Analogie im Alltag
Stellen Sie sich den Bestellvorgang in einem Restaurant vor:
- Kunde(Client) muss nicht wissen, wie der Koch das Gericht zubereitet
- Kellner(Invoker) nimmt die Bestellung entgegen, kocht aber nicht selbst
- Bestellung(Command-Objekt) enthält die spezifischen Gerichtsinformationen
- Koch(Receiver) führt die eigentlichen Kochvorgänge basierend auf der Bestellung aus
Dieses gut aufgeteilte Muster ist eine reale Erscheinungsform des Command-Patterns.
Warum braucht man das Command-Pattern?
Einschränkungen des traditionellen Ansatzes
In der traditionellen Programmierung rufen wir normalerweise direkt die Methoden eines Objekts auf:
Beispiel
def turn_on(self):
print("Das Licht ist an")
def turn_off(self):
print("Das Licht ist aus")
# Direkter Aufruf
light = Light()
light.turn_on()
light.turn_off()
Dieser Ansatz hat mehrere Probleme:
- Enge Kopplung: Der Aufrufer muss die spezifische Schnittstelle des Empfängers kennen
- Schwer erweiterbar: Das Hinzufügen neuer Funktionen erfordert die Änderung vorhandenen Codes
- Keine Unterstützung für Undo/Redo: Operationen können nach der Ausführung nicht rückgängig gemacht werden
Vorteile des Command-Musters

Das Command-Muster löst die oben genannten Probleme durch die Einführung einer Zwischenschicht (Befehlsobjekt) und bietet:
- Entkopplung: Keine direkte Abhängigkeit zwischen Aufrufer und Empfänger
- Erweiterbarkeit: Einfaches Hinzufügen neuer Befehle
- Unterstützung für Undo/Redo: Der Operationsverlauf kann aufgezeichnet werden
- Warteschlangenunterstützung: Befehle können für eine verzögerte Ausführung in eine Warteschlange gestellt werden
Kernkomponenten des Befehlsmusters
Grundstruktur
Das Befehlsmuster umfasst vier Kernrollen:
| Rolle | Verantwortlichkeit | Beispiel |
|---|---|---|
| Command(Befehlsschnittstelle) | Deklariert die Schnittstelle zum Ausführen von Operationen | execute(), undo() |
| ConcreteCommand(Konkreter Befehl) | Implementiert die Befehlsschnittstelle und bindet den Empfänger | LightOnCommand, LightOffCommand |
| Receiver(Empfänger) | Weiß, wie Operationen ausgeführt werden | Light, TV |
| Invoker(Aufrufer) | Speichert und führt Befehle aus | RemoteControl |
Vollständiges Codebeispiel
Grundlegende Implementierung
Lass uns das Befehlsmuster anhand eines Beispiels zur Beleuchtungssteuerung im Smart Home verstehen:
Beispiel
from typing import List
# 1. Befehlsschnittstelle
class Command(ABC):
@abstractmethod
def execute(self):
pass
@abstractmethod
def undo(self):
pass
# 2. Empfänger - Lichtgerät
class Light:
def turn_on(self):
print("💡 Licht ist an")
def turn_off(self):
print("💡 Licht ist aus")
# 3. Konkreter Befehl - Befehl zum Einschalten des Lichts
class LightOnCommand(Command):
def __init__(self, light: Light):
self.light = light
def execute(self):
self.light.turn_on()
def undo(self):
self.light.turn_off()
# 4. Konkretes Kommando - Befehl zum Ausschalten des Lichts
class LightOffCommand(Command):
def __init__(self, light: Light):
self.light = light
def execute(self):
self.light.turn_off()
def undo(self):
self.light.turn_on()
# 5. Aufrufer - Fernbedienung
class RemoteControl:
def __init__(self):
self.command = None
self.history: List[Command] = []
def set_command(self, command: Command):
self.command = command
def press_button(self):
if self.command:
self.command.execute()
self.history.append(self.command)
def press_undo(self):
if self.history:
last_command = self.history.pop()
last_command.undo()
# Client-Code
if __name__ == "__main__":
# Gerät erstellen
living_room_light = Light()
# Befehle erstellen
light_on = LightOnCommand(living_room_light)
light_off = LightOffCommand(living_room_light)
# Fernbedienung erstellen
remote = RemoteControl()
# Test: Licht einschalten
print("=== Test: Licht einschalten ===")
remote.set_command(light_on)
remote.press_button()
# Test: Licht ausschalten
print("\n=== Test: Licht ausschalten ===")
remote.set_command(light_off)
remote.press_button()
# Test: Rückgängig machen
print("\n=== Test: Rückgängig-Operation ===")
remote.press_undo() # Ausschalten des Lichts rückgängig machen, sollte Licht einschalten
remote.press_undo() # Einschalten des Lichts rückgängig machen, sollte Licht ausschalten
Ausführungsergebnis:

Fortgeschrittenes Anwendungsbeispiel
Universelle Fernbedienung mit Unterstützung für mehrere Geräte
Lass uns das vorherige Beispiel erweitern und eine universelle Fernbedienung erstellen, die mehrere Geräte unterstützt:
Beispiel
class TV:
def __init__(self, location: str):
self.location = location
self.is_on = False
self.volume = 50
def turn_on(self):
self.is_on = True
print(f"📺 {self.location} TV ist eingeschaltet")
def turn_off(self):
self.is_on = False
print(f"📺 {self.location} TV ist ausgeschaltet")
def set_volume(self, volume: int):
self.volume = volume
print(f"📺 {self.location} TV-Lautstärke eingestellt auf {volume}")
# TV-bezogene Befehle
class TVOnCommand(Command):
def __init__(self, tv: TV):
self.tv = tv
self.previous_volume = 50
def execute(self):
self.previous_volume = self.tv.volume
self.tv.turn_on()
def undo(self):
self.tv.turn_off()
self.tv.volume = self.previous_volume
class TVVolumeUpCommand(Command):
def __init__(self, tv: TV):
self.tv = tv
self.previous_volume = 50
def execute(self):
self.previous_volume = self.tv.volume
self.tv.set_volume(min(100, self.tv.volume + 10))
def undo(self):
self.tv.set_volume(self.previous_volume)
# Makrobefehl - mehrere Befehle mit einem Tastendruck ausführen
class MacroCommand(Command):
def __init__(self, commands: List[Command]):
self.commands = commands
def execute(self):
for command in self.commands:
command.execute()
def undo(self):
# Rückgängig in umgekehrter Reihenfolge
for command in reversed(self.commands):
command.undo()
# Verbesserte Fernbedienung
class AdvancedRemoteControl:
def __init__(self, slot_count: int = 4):
self.on_commands: List[Command] = [None] * slot_count
self.off_commands: List[Command] = [None] * slot_count
self.history: List[Command] = []
def set_command(self, slot: int, on_command: Command, off_command: Command):
self.on_commands[slot] = on_command
self.off_commands[slot] = off_command
def press_on_button(self, slot: int):
if self.on_commands[slot]:
self.on_commands[slot].execute()
self.history.append(self.on_commands[slot])
def press_off_button(self, slot: int):
if self.off_commands[slot]:
self.off_commands[slot].execute()
self.history.append(self.off_commands[slot])
def press_undo(self):
if self.history:
last_command = self.history.pop()
last_command.undo()
# Test der erweiterten Fernbedienung
def test_advanced_remote():
print("=== Test der universellen Fernbedienung ===")
# Geräte erstellen
living_room_light = Light()
bedroom_tv = TV("Schlafzimmer")
# Befehle erstellen
light_on = LightOnCommand(living_room_light)
light_off = LightOffCommand(living_room_light)
tv_on = TVOnCommand(bedroom_tv)
tv_volume_up = TVVolumeUpCommand(bedroom_tv)
# Makrobefehl erstellen - Kinomodus
cinema_mode = MacroCommand([light_off, tv_on, tv_volume_up])
# Fernbedienung einrichten
remote = AdvancedRemoteControl()
remote.set_command(0, light_on, light_off) # Slot 0: Lichtsteuerung
remote.set_command(1, tv_on, TVOnCommand(bedroom_tv)) # Slot 1: TV-Steuerung
remote.set_command(2, cinema_mode, light_on) # Slot 2: Kinomodus
# Verschiedene Funktionen testen
print("\n1. Licht einschalten:")
remote.press_on_button(0)
print("\n2. Kinomodus aktivieren:")
remote.press_on_button(2)
print("\n3. Operation rückgängig machen:")
remote.press_undo()
if __name__ == "__main__":
test_advanced_remote()
Parameter und Optionen des Command-Musters
Erweiterung der Command-Schnittstelle
In praktischen Anwendungen kann die Command-Schnittstelle bei Bedarf um weitere Funktionalität erweitert werden:
Beispiel
@abstractmethod
def execute(self):
"""Befehl ausführen"""
pass
@abstractmethod
def undo(self):
"""Befehl rückgängig machen"""
pass
@abstractmethod
def redo(self):
"""Redo-Befehl"""
pass
@abstractmethod
def can_execute(self) -> bool:
"""Prüfen, ob der Befehl ausgeführt werden kann"""
pass
@abstractmethod
def get_description(self) -> str:
"""Befehlsbeschreibung abrufen"""
pass
Befehlswarteschlange und verzögerte Ausführung
Das Command-Muster unterstützt auf natürliche Weise Befehlswarteschlangen, was in vielen Szenarien sehr nützlich ist:
Beispiel
def __init__(self):
self.queue: List[Command] = []
def add_command(self, command: Command):
self.queue.append(command)
def process_commands(self):
while self.queue:
command = self.queue.pop(0)
if command.can_execute():
command.execute()
def clear(self):
self.queue.clear()
Praktische Übungen
Übung 1: Implementieren Sie ein Klimaanlagen-Steuerungssystem
Versuchen Sie, das Command-Muster für ein Klimaanlagen-Steuerungssystem zu implementieren:
Beispiel
class AirConditioner:
def __init__(self):
self.temperature = 26
self.is_on = False
def turn_on(self):
# Einschalten der Klimaanlage implementieren
pass
def turn_off(self):
# Ausschalten der Klimaanlage implementieren
pass
def set_temperature(self, temp: int):
# Einstellen der Temperatur implementieren
pass
# Befehlsklassen für die Klimaanlage implementieren
# AirConditionerOnCommand
# AirConditionerOffCommand
# TemperatureUpCommand
# TemperatureDownCommand
Übung 2: Undo-Funktionalität für einen Texteditor implementieren
Erstelle einen einfachen Texteditor, der Texteingabe und Undo-Operationen unterstützt:
Beispiel
def __init__(self):
self.content = ""
def add_text(self, text: str):
# Hinzufügen von Text implementieren
pass
def delete_text(self, length: int):
# Löschen von Text implementieren
pass
# Textbearbeitungsbefehle implementieren
# AddTextCommand
# DeleteTextCommand
FAQ
F: Für welche Szenarien eignet sich das Befehlsmuster?
A:Das Befehlsmuster eignet sich besonders für die folgenden Szenarien:
- Wenn Operationen parametrisiert werden müssen (z. B. Schaltflächen, die an verschiedene Funktionen gebunden sind)
- Wenn Rückgängig-/Wiederholen-Funktionalität benötigt wird
- Wenn Operationen für eine verzögerte Ausführung in eine Warteschlange gestellt werden müssen
- Wenn Operationsprotokolle aufgezeichnet werden müssen
- Wenn Transaktionsoperationen unterstützt werden müssen
F: Was sind die Nachteile des Befehlsmusters?
A:Die wichtigsten Nachteile sind:
- Es kann eine große Anzahl von Befehlsklassen entstehen
- Es erhöht die Codekomplexität
- Für einfache Operationen kann es zu umständlich sein
F: Wie entscheidet man, ob das Befehlsmuster verwendet werden soll?
A:Berücksichtigen Sie die folgenden Punkte:
- Wenn Sie nur einfache Methodenaufrufe benötigen, verwenden Sie das Befehlsmuster nicht
- Wenn Rückgängig-/Wiederholen-Funktionalität benötigt wird, wird es dringend empfohlen
- Wenn das System eine hohe Entkopplung erfordert, ist das Befehlsmuster eine gute Wahl
- Wenn Operationen in eine Warteschlange gestellt oder protokolliert werden müssen, ist das Befehlsmuster sehr gut geeignet
Zusammenfassung
Das Befehlsmuster entkoppelt den Aufrufer vom Empfänger, indem Operationen als Objekte gekapselt werden, und bietet leistungsstarke Erweiterbarkeit. Obwohl es die Codekomplexität etwas erhöht, ist das Befehlsmuster in Szenarien wie Rückgängig/Wiederholen, Operationswarteschlangen und Protokollierung ein unverzichtbares Entwurfsmuster.
Wichtige Punkte:
- Das Command-Muster trennt "was zu tun ist" von "wer es tut"
- Unterstützt erweiterte Funktionen wie Rückgängig, Wiederholen und Warteschlangen
- Einfach mit neuen Befehlen erweiterbar
- Weit verbreitet in GUI-Anwendungen, Transaktionssystemen und der Spieleentwicklung