Menu
Coddy logo textTech

Паттерн Состояние

Часть раздела Object Oriented Programming путешествия по Python на Coddy. Урок 53 из 64.

Паттерн Состояние (State) позволяет объекту изменять свое поведение при изменении его внутреннего состояния. Создается впечатление, что объект изменил свой класс в зависимости от его текущего состояния.

Вот простые классы состояний для светофора:

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"

Каждое состояние определяет, что происходит при переходе в следующее состояние.

Создайте класс контекста, который хранит текущее состояние:

class TrafficLight:
    def __init__(self):
        self.state = RedState()  # Начинаем с красного
    
    def change(self):
        self.state.next_state(self)
    
    def get_color(self):
        return self.state.current_color()

Светофор делегирует поведение своему текущему объекту состояния.

Используйте светофор:

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

light.change()  # Красный -> Зеленый
print(f"Current: {light.get_color()}")

light.change()  # Зеленый -> Желтый
print(f"Current: {light.get_color()}")

light.change()  # Желтый -> Красный
print(f"Current: {light.get_color()}")

Создайте еще один пример с простым плеером:

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()   # Запуск музыки
player.play()   # Уже воспроизводится
player.stop()   # Остановка музыки
player.stop()   # Уже остановлено

Вывод:

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

Ключевой момент: Паттерн «Состояние» (State Pattern) инкапсулирует поведение, зависящее от состояния, в отдельные классы и позволяет объекту контекста делегировать выполнение текущему состоянию. При изменении состояния поведение меняется автоматически. Это избавляет от сложных конструкций if/else и упрощает добавление новых состояний.

challenge icon

Задание

Легко

В этом испытании вы реализуете класс State в файле state.py с использованием правильной инкапсуляции и методов. Файл содержит подробные комментарии TODO, которые помогут вам выполнить реализацию шаг за шагом.

Набор тестов разработан для проверки того, что ваша реализация соответствует всем требованиям и правильно обрабатывает исключительные ситуации. Используйте результаты тестов, чтобы понять ожидаемое поведение и доработать свое решение.

Попробуйте сами

from state import State

# Комплексный обработчик тестовых случаев
test_case = input()

if test_case == "basic_test":
    obj = State("Test Name")
    obj.display_info()

elif test_case == "validation_test":
    obj = State("Validation Test")
    print(f"Name: {obj.name}")

elif test_case == "empty_name_test":
    obj = State("")
    obj.display_info()
    print(f"Name property: {obj.name}")

elif test_case == "special_chars_test":
    obj = State("Test@#$%")
    obj.display_info()
    print(f"Name property: {obj.name}")

elif test_case == "long_name_test":
    long_name = "A" * 100
    obj = State(long_name)
    obj.display_info()
    print(f"Name length: {len(obj.name)}")

elif test_case == "multiple_states_test":
    states = [
        State("California"),
        State("Texas"),
        State("New York"),
        State("Florida")
    ]
    
    for state in states:
        state.display_info()
        print(f"Name via property: {state.name}")

elif test_case == "attribute_access_test":
    obj = State("Access Test")
    # Прямой доступ к приватному атрибуту (не рекомендуется)
    print(f"Direct access (not recommended): {obj._name}")
    # Доступ через свойство (рекомендуется)
    print(f"Property access (recommended): {obj.name}")

elif test_case == "property_behavior_test":
    obj = State("Original Name")
    print(f"Original name: {obj.name}")
    
    # Попытка изменить имя (завершится ошибкой, так как сеттер отсутствует)
    try:
        obj.name = "New Name"
    except AttributeError as e:
        print(f"Error when trying to modify name: {e}")
    
    # Показать, что имя не изменилось
    print(f"Name after modification attempt: {obj.name}")
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Object Oriented Programming

Потренируйтесь самостоятельно: Онлайн-компилятор Python