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, объект сам управляет переходами между состояниями.
Проверьте себя
1. Что делает паттерн State?
AСоздаёт один экземпляр
BМеняет поведение объекта при смене его внутреннего состояния
CКопирует объект
DАдаптирует интерфейс
2. Чем State отличается от Strategy?
AНичем
BВ State объект сам переключает состояния, которые знают о переходах; в Strategy алгоритм выбирает клиент
CState быстрее
DStrategy не использует объекты
3. Какой код заменяет State?
AЦиклы for
BВетвистые проверки if по флагу состояния, разбросанные по методам
CИмпорты
DСписочные включения
Поддержать проект