Menu
Coddy logo textTech

Command-Muster

Teil des Abschnitts Object Oriented Programming der Python-Journey von Coddy — Lektion 49 von 64.

Das Command Pattern kapselt eine Anfrage als Objekt, was es ermöglicht, Operationen in eine Warteschlange zu stellen, Anfragen zu protokollieren und eine Rückgängig-Funktionalität zu unterstützen. Es trennt das Objekt, das die Operation aufruft, von demjenigen, das sie ausführt.

Hier sind einfache Befehlsklassen:

class Command:
    def execute(self):
        pass

class LightOnCommand(Command):
    def __init__(self, light):
        self.light = light
    
    def execute(self):
        self.light.turn_on()

class LightOffCommand(Command):
    def __init__(self, light):
        self.light = light
    
    def execute(self):
        self.light.turn_off()

Jeder Befehl kapselt eine spezifische Operation auf einem Empfängerobjekt.

Erstellen Sie den Receiver, der die eigentliche Arbeit verrichtet:

class Light:
    def turn_on(self):
        print("Light is on")
    
    def turn_off(self):
        print("Light is off")

Erstellen Sie einen Invoker, der Befehle ausführt:

class RemoteControl:
    def __init__(self):
        self.command = None
    
    def set_command(self, command):
        self.command = command
    
    def press_button(self):
        self.command.execute()

Verwenden Sie das Command-Pattern:

# Empfänger erstellen
light = Light()

# Befehle erstellen
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)

# Aufrufer (Invoker) erstellen
remote = RemoteControl()

# Verschiedene Befehle ausführen
remote.set_command(light_on)
remote.press_button()

remote.set_command(light_off)
remote.press_button()

Unterstützung für Rückgängig-Operationen hinzufügen:

class UndoableCommand(Command):
    def undo(self):
        pass

class LightOnCommand(UndoableCommand):
    def __init__(self, light):
        self.light = light
    
    def execute(self):
        self.light.turn_on()
    
    def undo(self):
        self.light.turn_off()

class SmartRemote:
    def __init__(self):
        self.last_command = None
    
    def execute_command(self, command):
        command.execute()
        self.last_command = command
    
    def undo(self):
        if self.last_command:
            self.last_command.undo()

smart_remote = SmartRemote()
smart_remote.execute_command(LightOnCommand(light))
smart_remote.undo()  # Schaltet das Licht aus

Ausgabe:

Light is on
Light is off
Light is on
Light is off

Kernpunkt: Das Command-Pattern wandelt Anfragen in Objekte um, die gespeichert, weitergegeben und später ausgeführt werden können. Der Aufrufer (Invoker) muss nicht wissen, wie die Operation ausgeführt wird – er ruft lediglich execute() auf dem Befehlsobjekt auf. Dies ermöglicht Funktionen wie Rückgängigmachen/Wiederholen, das Einreihen von Operationen in Warteschlangen und das Protokollieren von Befehlen.

challenge icon

Aufgabe

Einfach

In dieser Herausforderung implementieren Sie die grundlegende Command-Basisklasse in command.py — den wesentlichen Baustein des Command-Patterns. Diese Übung konzentriert sich speziell auf die Kapselung: das private Speichern von Daten und deren sichere Bereitstellung über ein Property.

Ändern Sie nur command.py gemäß den TODO-Kommentaren. Die TODO-Kommentare führen Sie dazu:

  • Speichern Sie den Befehlsnamen als privates Attribut (_name)
  • Stellen Sie ihn über ein schreibgeschütztes Property (name) bereit
  • Implementieren Sie eine display_info()-Methode, die den Befehlsnamen ausgibt

Hinweis: Diese Herausforderung deckt nur die Basisklasse Command ab — Invoker, Receiver und die Undo-Funktionalität werden in späteren Lektionen eingeführt. Das Abschließen dieses Schritts bietet Ihnen die Kapselungsgrundlage, auf der das vollständige Pattern aufbaut.

Ihre Implementierung wird durch driver.py getestet, welche Folgendes validiert:

  • Grundlegende Funktionalität und Ausgabeformat
  • Grenzfälle (leere Eingaben, Sonderzeichen, lange Namen)
  • Schutz des schreibgeschützten Propertys (der Versuch obj.name = ... sollte einen AttributeError auslösen)
  • Vorhandensein des privaten Attributs _name

Spickzettel

Das Command-Pattern kapselt eine Anfrage als Objekt und ermöglicht es Ihnen, Operationen in eine Warteschlange zu stellen, Anfragen zu protokollieren und eine Rückgängig-Funktionalität zu unterstützen.

Grundlegende Befehlsstruktur:

class Command:
    def execute(self):
        pass

class LightOnCommand(Command):
    def __init__(self, light):
        self.light = light
    
    def execute(self):
        self.light.turn_on()

class LightOffCommand(Command):
    def __init__(self, light):
        self.light = light
    
    def execute(self):
        self.light.turn_off()

Erstellen Sie einen Empfänger (Receiver), der die eigentliche Arbeit verrichtet:

class Light:
    def turn_on(self):
        print("Light is on")
    
    def turn_off(self):
        print("Light is off")

Erstellen Sie einen Aufrufer (Invoker), der Befehle ausführt:

class RemoteControl:
    def __init__(self):
        self.command = None
    
    def set_command(self, command):
        self.command = command
    
    def press_button(self):
        self.command.execute()

Anwendungsbeispiel:

# Empfänger erstellen
light = Light()

# Befehle erstellen
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)

# Aufrufer erstellen
remote = RemoteControl()

# Verschiedene Befehle ausführen
remote.set_command(light_on)
remote.press_button()

remote.set_command(light_off)
remote.press_button()

Rückgängig-Funktionalität hinzufügen:

class UndoableCommand(Command):
    def undo(self):
        pass

class LightOnCommand(UndoableCommand):
    def __init__(self, light):
        self.light = light
    
    def execute(self):
        self.light.turn_on()
    
    def undo(self):
        self.light.turn_off()

class SmartRemote:
    def __init__(self):
        self.last_command = None
    
    def execute_command(self, command):
        command.execute()
        self.last_command = command
    
    def undo(self):
        if self.last_command:
            self.last_command.undo()

Probier es selbst

# Importiere die Command-Klasse aus command.py
from command import Command

# Umfassender Testfall-Handler
test_case = input()

if test_case == "basic_test":
    obj = Command("Test Name")
    obj.display_info()
elif test_case == "validation_test":
    obj = Command("Validation Test")
    print(f"Name: {obj.name}")
elif test_case == "empty_name_test":
    obj = Command("")
    obj.display_info()
    print(f"Empty name handled: {'Yes' if obj.name == '' else 'No'}")
elif test_case == "property_access_test":
    obj = Command("Property Test")
    original_name = obj.name
    try:
        # Dies sollte fehlschlagen, da name eine schreibgeschützte Eigenschaft ist
        obj.name = "Modified Name"
        print("Property protection failed")
    except AttributeError:
        print("Property protected successfully")
    print(f"Name unchanged: {obj.name == original_name}")
elif test_case == "multiple_commands_test":
    commands = [
        Command("First Command"),
        Command("Second Command"),
        Command("Third Command")
    ]
    for cmd in commands:
        cmd.display_info()
elif test_case == "attribute_test":
    obj = Command("Attribute Test")
    has_private_name = hasattr(obj, "_name")
    print(f"Has _name attribute: {has_private_name}")
elif test_case == "special_chars_test":
    obj = Command("!@#$%^&*()_+{}[]|\\:;\"'<>,.?/")
    obj.display_info()
elif test_case == "long_name_test":
    long_name = "A" * 100
    obj = Command(long_name)
    obj.display_info()
    print(f"Name length: {len(obj.name)}")
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Object Oriented Programming