Menu
Coddy logo textTech

Wzorzec Polecenie

Część sekcji Programowanie obiektowe ścieżki Python w Coddy. Lekcja 49 z 64.

Wzorzec Polecenia zamienia żądanie w obiekt, umożliwiając kolejkowanie operacji, rejestrowanie żądań i obsługę funkcji cofania. Oddziela obiekt wywołujący operację od obiektu, który ją wykonuje.

Oto proste klasy poleceń:

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()

Każde polecenie hermetyzuje określoną operację na obiekcie odbiornika.

Utwórz odbiornik, który wykonuje właściwą pracę:

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

Utwórz wywołujący, który wykonuje polecenia:

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

Użyj wzorca polecenia:

# Utwórz odbiorcę
light = Light()

# Utwórz polecenia
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)

# Utwórz wywołującego
remote = RemoteControl()

# Wykonaj różne polecenia
remote.set_command(light_on)
remote.press_button()

remote.set_command(light_off)
remote.press_button()

Dodaj obsługę operacji cofania:

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()  # Wyłącza światło

Wynik:

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

Kluczowa kwestia: Wzorzec Polecenie przekształca żądania w obiekty, które można przechowywać, przekazywać i wykonywać później. Wywołujący nie musi wiedzieć, jak wykonać operację — po prostu wywołuje execute() na obiekcie polecenia. Umożliwia to takie funkcje jak cofanie i ponawianie, kolejkowanie operacji oraz rejestrowanie poleceń.

challenge icon

Wyzwanie

Łatwy

W tym wyzwaniu zaimplementujesz podstawową klasę bazową Command w pliku command.py: niezbędny element wzorca Command. To ćwiczenie koncentruje się konkretnie na enkapsulacji: przechowywaniu danych prywatnie i bezpiecznym udostępnianiu ich za pomocą właściwości.

Modyfikuj wyłącznie plik command.py, zgodnie z komentarzami TODO. Komentarze TODO podpowiedzą Ci, jak:

  • Przechowywać nazwę polecenia jako prywatny atrybut (_name)
  • Udostępnić ją za pomocą właściwości tylko do odczytu (name)
  • Zaimplementować metodę display_info(), która wyświetla nazwę polecenia

Uwaga: To wyzwanie obejmuje wyłącznie bazową klasę Command: obiekt wywołujący, odbiorca i funkcja cofania zostaną wprowadzone w kolejnych lekcjach. Wykonanie tego kroku zapewni Ci podstawy enkapsulacji, na których opiera się cały wzorzec.

Twoja implementacja zostanie przetestowana przez driver.py, który sprawdza:

  • Podstawowe działanie i format wyjściowy
  • Przypadki brzegowe (puste dane wejściowe, znaki specjalne, długie nazwy)
  • Ochronę właściwości tylko do odczytu (próba wykonania obj.name = ... powinna wywołać wyjątek AttributeError)
  • Obecność prywatnego atrybutu _name

Spróbuj swoich sił

# Zaimportuj klasę Command z command.py
from command import Command

# Kompleksowa obsługa przypadków testowych
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:
        # To powinno się nie powieść, ponieważ name jest właściwością tylko do odczytu
        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 iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Python online