Паттерн «Состояние»
Часть раздела Объектно-ориентированное программирование путешествия по Python на Coddy. Урок 53 из 64.
Паттерн «Состояние» позволяет объекту изменять своё поведение при изменении его внутреннего состояния. Кажется, что класс объекта меняется в зависимости от его текущего состояния.
Вот простые классы состояний для светофора:
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Ключевой момент: паттерн «Состояние» инкапсулирует поведение, специфичное для каждого состояния, в отдельных классах и позволяет объекту контекста делегировать задачи текущему состоянию. Когда состояние меняется, поведение меняется автоматически. Это избавляет от сложных операторов if/else и упрощает добавление новых состояний.
Задание
ЛегкоРеализуй паттерн «Состояние» в state.py для лампы с одной кнопкой. Каждое нажатие переключает лампу между тремя состояниями по циклу: выключена, слабое свечение, яркое свечение, а затем снова выключена. Вместо цепочки if с проверкой строки каждое состояние представлено собственным объектом, который знает, что происходит при нажатии кнопки.
LampState— базовый класс с методамиpress(lamp)иdescribe().OffState,LowStateиHighStateреализуют эти методы:pressвызываетlamp.set_state(...), передавая следующее состояние (из выключенного в состояние слабого свечения, из слабого свечения — в яркое, из яркого — в выключенное), аdescribeвозвращаетLamp is off,Lamp is on lowилиLamp is on high.Lamp— контекст: он начинает работу в состоянииOffState,set_stateзаменяет объект состояния,press_buttonделегирует действие текущему состоянию, аstatusвозвращает текущее описание.
driver.py заблокирован: он нажимает кнопку и выводит статус. Обрати внимание: Lamp никогда не проверяет, в каком состоянии находится; это решают объекты состояний.
Попробуйте сами
from state import Lamp
# Обработчик тестового случая (не изменять)
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())
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Введение в ООПВнешние файлыКлассы и объектыПараметр selfМетодыАтрибутыМетод-конструктор (__init__)Повторение — простой калькулятор4Наследование
Основы наследованияФункция super()Переопределение методовМножественное наследованиеПорядок разрешения методовПовторение: иерархия сотрудников7Специальные методы
Введение в магические методыПерегрузка операторовМагические методы контейнеровПовторение — пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн «Одиночка»Паттерн «Фабрика»Паттерн «Наблюдатель»Паттерн «Стратегия»2Декораторы
Введение в декораторыДекоратор свойстваДекоратор статического методаДекоратор метода класса5Полиморфизм
Переопределение методов: повторениеУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовПовторение — калькулятор фигур8Продвинутые концепции ООП
Композиция и наследованиеМиксиныСтатические методы и методы классаДекораторы классовМенеджеры контекста11Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»3Свойства классов
Переменные экземпляра и классаДекораторы свойствПриватные атрибутыПовторение — менеджер банковского счёта6Инкапсуляция
Публичные, защищённые и приватные членыМодификаторы доступаСокрытие информацииПродвинутые декораторы свойствПовторение — система учёта студентов12Проект: Система управления библиотекой
Обзор проектаКлассы книг и пользователейСистема выдачи книгФункция поискаИнтерфейс администратораТестирование и интеграцияПотренируйтесь самостоятельно: Онлайн-компилятор Python