State дозволяє об'єкту змінювати поведінку залежно від внутрішнього стану так, ніби він змінив клас.
Яку проблему вирішує
Об'єкти зі складним життєвим циклом (замовлення, підписка, оплата) обростають розгалуженнями if ($status === ...) у кожному методі. Логіка одного стану розмазана по сервісах, а додавання нового стану зачіпає всі ці місця.
Як вирішує
Кожен стан стає окремим класом із власними правилами. Об'єкт делегує поведінку поточному стану, а той сам вирішує, у який стан перейти далі. Розгалуження if/switch зникають.
interface OrderState
{
public function pay(Order $order): void;
}
class PendingState implements OrderState
{
public function pay(Order $order): void
{
$order->state = new PaidState();
}
}
class PaidState implements OrderState
{
public function pay(Order $order): void
{
throw new LogicException('Already paid');
}
}
class Order
{
public function __construct(public OrderState $state) {}
public function pay(): void
{
$this->state->pay($this);
}
}
Де застосовувати
- Замовлення, підписки, оплати, процеси з чіткими переходами між станами.
- У Laravel-екосистемі для моделей зручний пакет
spatie/laravel-model-states.
Плюси та мінуси
- + Прибирає каскади
if/switch; логіка кожного стану зосереджена в одному класі.
- + Переходи прозорі - поточний стан сам вирішує, у який наступний перейти.
- − Більше класів і непрямий потік керування.
- − Споріднений зі Strategy, але State ще й керує переходами між собою. Якщо станів два і переходи тривіальні - enum + проста перевірка читабельніші за ієрархію класів.