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łoWynik:
Light is on
Light is off
Light is on
Light is offKluczowa 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ń.
Wyzwanie
ŁatwyW 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ątekAttributeError) - 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)}")Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektyParametr selfMetodyAtrybutyMetoda konstruktora (__init__)Powtórzenie – prosty kalkulator4Dziedziczenie
Podstawy dziedziczeniaFunkcja super()Nadpisywanie metodDziedziczenie wielokrotneKolejność rozwiązywania metodPodsumowanie – hierarchia pracowników7Metody specjalne
Wprowadzenie do metod magicznychPrzeciążanie operatorówMagiczne metody kontenerówPowtórka – własna lista10Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec Strategy2Dekoratory
Wprowadzenie do dekoratorówDekorator propertyDekorator metody statycznejDekorator metody klasowej5Polimorfizm
Ponowne omówienie nadpisywania metodTypowanie kaczeKlasy i metody abstrakcyjneProjektowanie interfejsówPodsumowanie — kalkulator kształtów8Zaawansowane koncepcje programowania obiektowego
Kompozycja a dziedziczenieMiksyMetody statyczne i klasoweDekoratory klasMenedżery kontekstu11Wzorce projektowe, część 2
Wzorzec PolecenieWzorzec AdapterWzorzec DekoratorWzorzec Metoda szablonowaWzorzec StanWzorzec Kompozyt3Właściwości klas
Zmienne instancji a zmienne klasoweDekoratory właściwościAtrybuty prywatnePowtórzenie — menedżer kont bankowych6Hermetyzacja
Public, Protected, Private — pamięćModyfikatory dostępuUkrywanie informacjiDekoratory właściwości — zaawansowanePodsumowanie — system ewidencji uczniów12Projekt: System zarządzania biblioteką
Przegląd projektuKlasy Book i UserSystem wypożyczeńFunkcja wyszukiwaniaInterfejs administratoraTestowanie i integracjaPoćwicz samodzielnie: Kompilator Python online