Menu
Coddy logo textTech

Wzorzec Stan

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

Wzorzec Stan pozwala obiektowi zmieniać zachowanie, gdy zmienia się jego stan wewnętrzny. Obiekt sprawia wrażenie, jakby zmieniał swoją klasę w zależności od bieżącego stanu.

Oto proste klasy stanów dla sygnalizacji świetlnej:

class RedState:
    def next_state(self, light):
        print("Red -> Green")
        light.state = GreenState()
    
    def current_color(self):
        return "Red"

class GreenState:
    def next_state(self, light):
        print("Green -> Yellow")
        light.state = YellowState()
    
    def current_color(self):
        return "Green"

class YellowState:
    def next_state(self, light):
        print("Yellow -> Red")
        light.state = RedState()
    
    def current_color(self):
        return "Yellow"

Każdy stan określa, co dzieje się podczas przejścia do następnego stanu.

Utwórz klasę kontekstu, która przechowuje bieżący stan:

class TrafficLight:
    def __init__(self):
        self.state = RedState()  # Zacznij od czerwonego
    
    def change(self):
        self.state.next_state(self)
    
    def get_color(self):
        return self.state.current_color()

Sygnalizacja świetlna deleguje działanie do swojego bieżącego obiektu stanu.

Użyj sygnalizacji świetlnej:

light = TrafficLight()
print(f"Current: {light.get_color()}")

light.change()  # Czerwone -> Zielone
print(f"Current: {light.get_color()}")

light.change()  # Zielone -> Żółte
print(f"Current: {light.get_color()}")

light.change()  # Żółte -> Czerwone
print(f"Current: {light.get_color()}")

Utwórz kolejny przykład z prostym odtwarzaczem:

class PlayingState:
    def play(self, player):
        print("Already playing")
    
    def stop(self, player):
        print("Stopping music")
        player.state = StoppedState()

class StoppedState:
    def play(self, player):
        print("Starting music")
        player.state = PlayingState()
    
    def stop(self, player):
        print("Already stopped")

class MusicPlayer:
    def __init__(self):
        self.state = StoppedState()
    
    def play(self):
        self.state.play(self)
    
    def stop(self):
        self.state.stop(self)

player = MusicPlayer()
player.play()   # Uruchamianie muzyki
player.play()   # Muzyka już gra
player.stop()   # Zatrzymywanie muzyki
player.stop()   # Muzyka jest już zatrzymana

Wynik:

Current: Red
Red -> Green
Current: Green
Green -> Yellow
Current: Yellow
Yellow -> Red
Current: Red
Starting music
Already playing
Stopping music
Already stopped

Najważniejsza informacja: Wzorzec Stan hermetyzuje zachowanie specyficzne dla danego stanu w oddzielnych klasach i pozwala obiektowi kontekstu delegować działania do bieżącego stanu. Gdy stan się zmienia, zachowanie zmienia się automatycznie. Eliminuje to złożone instrukcje if/else i ułatwia dodawanie nowych stanów.

challenge icon

Wyzwanie

Łatwy

Zaimplementuj wzorzec State w pliku state.py dla lampy z jednym przyciskiem. Każde naciśnięcie przełącza lampę cyklicznie między trzema stanami: wyłączona, niski poziom, wysoki poziom, a następnie z powrotem na wyłączoną. Zamiast łańcucha if sprawdzającego ciąg znaków każdy stan jest osobnym obiektem, który wie, co się dzieje po naciśnięciu przycisku.

  • LampState to klasa bazowa z metodami press(lamp) i describe().
  • OffState, LowState i HighState implementują te metody: press wywołuje lamp.set_state(...) z następnym stanem (z wyłączonej na niski poziom, z niskiego poziomu na wysoki, z wysokiego poziomu na wyłączoną), a describe zwraca Lamp is off, Lamp is on low lub Lamp is on high.
  • Lamp to kontekst: rozpoczyna działanie w stanie OffState, set_state podmienia obiekt stanu, press_button deleguje obsługę do bieżącego stanu, a status zwraca bieżący opis.

Plik driver.py jest zablokowany: naciska przycisk i wyświetla status. Zwróć uwagę, że Lamp nigdy nie sprawdza, w jakim jest stanie; decydują o tym obiekty stanów.

Spróbuj swoich sił

from state import Lamp

# Obsługa przypadku testowego (nie zmieniać)
test_case = input()
lamp = Lamp()

if test_case == "initial_test":
    print(lamp.status())
elif test_case == "one_press_test":
    lamp.press_button()
    print(lamp.status())
elif test_case == "cycle_test":
    for _ in range(4):
        print(lamp.status())
        lamp.press_button()
elif test_case == "many_presses_test":
    for _ in range(7):
        lamp.press_button()
    print(lamp.status())
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