State
Паттерн конечного автомата: объект ведёт себя по-разному в зависимости от внутреннего состояния — без громоздких if по флагам.
State позволяет объекту менять поведение при изменении внутреннего состояния так, будто меняется его класс.
Какую задачу решает
Заказ бывает «новый», «оплачен», «отправлен», «доставлен», и в каждом состоянии действия разрешены разные. Если писать это через флаги и if status == ... в каждом методе, получаются ветвистые проверки, дублирующиеся повсюду. State выносит поведение каждого состояния в отдельный класс, а объект просто делегирует текущему состоянию и переключает его.
Идея и реализация
class State:
def next(self, order):
raise NotImplementedError
def name(self):
return type(self).__name__
class New(State):
def next(self, order):
order.state = Paid()
class Paid(State):
def next(self, order):
order.state = Shipped()
class Shipped(State):
def next(self, order):
print("Уже отправлен — дальше некуда")
class Order:
def __init__(self):
self.state = New()
def advance(self):
self.state.next(self)
print("Состояние:", self.state.name())
order = Order()
order.advance()
order.advance()
order.advance()
Вывод:
Состояние: Paid Состояние: Shipped Уже отправлен — дальше некуда Состояние: Shipped
Каждое состояние знает только про допустимые переходы из себя. Объект Order делегирует поведение текущему состоянию и не содержит ни одного if status. Добавить состояние «Возврат» — это новый класс, остальные не меняются.
Сравните это с наивным подходом «одно поле status и проверки в каждом методе». Там логика одного состояния размазана по всему классу: чтобы понять, что происходит в состоянии «оплачен», приходится вычитывать ветки if из десятка методов. С паттерном State всё поведение состояния собрано в одном классе — его легко прочитать, протестировать и изменить, не задев соседние состояния. Это прямая выгода принципа единственной ответственности.
Где обычно живут сами переходы — отдельный вопрос дизайна. В нашем примере состояние само назначает следующее (order.state = Paid()), и это удобно, когда граф переходов простой. Если же правил много и они сложные, переходы иногда выносят в отдельную таблицу или в объект-контекст, чтобы граф автомата был виден в одном месте, а не размазан по классам состояний.
State против Strategy
Классы выглядят одинаково (объект делегирует другому объекту), но смысл разный. В Strategy алгоритм выбирает клиент, и стратегии не знают друг о друге. В State объект сам переключает своё состояние, и состояния знают о переходах между собой.
Где встречается
Конечные автоматы: жизненный цикл заказа/задачи/документа, состояния TCP-соединения, парсеры и лексеры, поведение персонажей в играх, машины состояний UI (загрузка/успех/ошибка).
Итог
- State выносит поведение каждого состояния в отдельный класс.
- Убирает ветвистые
if statusи дублирование проверок. - В отличие от Strategy, объект сам управляет переходами между состояниями.