Паттерн Состояние
Часть раздела 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 и упрощает добавление новых состояний.
Задание
ЛегкоВ этом испытании вы реализуете класс 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}")В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыПараметр selfМетодыАтрибутыМетод-конструктор (__init__)Повторение — Простой калькулятор4Наследование
Базовое наследованиеФункция super()Переопределение методовМножественное наследованиеПорядок разрешения методовИтоги — Иерархия сотрудников7Специальные методы
Введение в магические методыПерегрузка операторовМагические методы контейнеровПовторение — Пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy5Полиморфизм
Снова о переопределении методовУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовИтоги — Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияМиксиныСтатические методы и методы классаДекораторы классовМенеджеры контекста11Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн Компоновщик3Свойства классов
Переменные экземпляра и классаДекораторы свойствПриватные атрибутыИтоги — Менеджер банковского счета6Инкапсуляция
Public, Protected, Private члены классаМодификаторы доступаСокрытие данныхДекораторы property: продвинутый уровеньИтоги: Система учета студентов12Проект: Управление библиотекой
Обзор проектаКлассы Book и User