Что это такое
Паттерн Состояние (State) позволяет объекту вести себя по-разному в разных состояниях, делая каждое состояние отдельным классом. Объект хранит своё текущее состояние и передаёт ему работу; переход в другое состояние означает просто замену значения.
Он заменяет растущую цепочку if/elif по полю статуса, повторяющуюся в половине методов, одним маленьким классом на каждое состояние.
Пример: жизнь заказа
Заказ сначала новый, затем оплаченный, затем отправленный. Каждый класс состояния отвечает на describe() по-своему и знает, какое состояние идёт следующим, — в самом заказе нет никакой логики статусов.
Каждое состояние — это класс
advance() навсегда остаётся одной строкой. Чтобы добавить состояние «доставлен», нужен новый класс и одно изменённое next_state() — никакой цепочки if/elif, в которой нужно что-то искать.
Вывод: полная программа на Python
from typing import Protocol
class State(Protocol):
def next_state(self) -> "State": ...
def describe(self) -> str: ...
class New:
def next_state(self) -> State:
return Paid()
def describe(self) -> str:
return "новый - ожидает оплаты"
class Paid:
def next_state(self) -> State:
return Shipped()
def describe(self) -> str:
return "оплачен - упаковываем коробку"
class Shipped:
def next_state(self) -> State:
return Shipped()
def describe(self) -> str:
return "отправлен - в пути"
class Order:
def __init__(self, state: State) -> None:
self.state = state
def advance(self) -> None:
self.state = self.state.next_state()
o = Order(New())
print(o.state.describe())
o.advance()
print(o.state.describe())
o.advance()
print(o.state.describe())
Когда его использовать
Используйте его, когда поведение объекта действительно зависит от состояния и у состояний есть настоящие правила переходов — заказы, документы (черновик/проверка/опубликован), соединения, игровые персонажи.
Не используйте его, когда статус только отображается и никогда не меняет поведение. Строкового поля и одного if/elif вполне достаточно, если состояний меньше трёх и правила простые.
Плюсы и минусы
Плюсы: правила каждого состояния находятся в одном маленьком классе; невозможные переходы просто не написаны; новые состояния расширяют код, а не изменяют его.
Минусы: много крошечных классов для простых автоматов; логика всего автомата разбросана по файлам — помогает схема в комментарии.