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, которые помогут вам выполнить реализацию шаг за шагом.

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

Шпаргалка

Паттерн «Состояние» (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 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()}")  # Current: Red
light.change()  # Red -> Green
print(f"Current: {light.get_color()}")  # Current: Green

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

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

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