Паттерн Цепочка обязанностей (Chain of Responsibility) в PHP
Короткий скринкаст: паттерн «Цепочка обязанностей» (Chain of Responsibility) — передаём запрос по цепочке обработчиков, пока один из них его не обработает. Согласуем заявку у тимлида и менеджера.
Описание
Цепочка обязанностей позволяет передавать запрос последовательно по цепочке обработчиков, пока один из них его не обработает. Заявку на сумму сначала получает тимлид: до 1000 он согласует её сам, иначе передаёт менеджеру. Менеджер согласует до 5000, а более крупную сумму дальше передавать некому — вернётся null. Клиент вызывает только первый обработчик и не знает, кто именно ответит.
UML-диаграмма
Код
Handler.php
interface Handler
{
public function setNext(Handler $next): Handler;
public function handle(int $amount): ?string;
}
AbstractHandler.php
abstract class AbstractHandler implements Handler
{
private ?Handler $next = null;
public function setNext(Handler $next): Handler
{
$this->next = $next;
return $next;
}
public function handle(int $amount): ?string
{
return $this->next?->handle($amount);
}
}
TeamLead.php
class TeamLead extends AbstractHandler
{
public function handle(int $amount): ?string
{
if ($amount <= 1000) {
return 'Team lead approved '.$amount;
}
return parent::handle($amount);
}
}
Manager.php
class Manager extends AbstractHandler
{
public function handle(int $amount): ?string
{
if ($amount <= 5000) {
return 'Manager approved '.$amount;
}
return parent::handle($amount);
}
}
demo.php
require_once 'Handler.php';
require_once 'AbstractHandler.php';
require_once 'TeamLead.php';
require_once 'Manager.php';
$teamLead = new TeamLead;
$manager = new Manager;
$teamLead->setNext($manager);
echo $teamLead->handle(500).PHP_EOL;
echo $teamLead->handle(3000).PHP_EOL;
var_dump($teamLead->handle(99999));
Пример результата
После php demo.php видно, как запрос идёт по цепочке: 500 согласует тимлид, 3000 — уже менеджер, а для 99999 подходящего обработчика нет и возвращается NULL:
Team lead approved 500
Manager approved 3000
NULL
Преимущества
- Отправитель не знает, какой обработчик в итоге обработает запрос.
- Обработчики можно добавлять, убирать и переупорядочивать, не меняя клиента.
- Каждый обработчик отвечает за одно условие (принцип единственной обязанности).
- Цепочка собирается динамически и может отличаться в разных сценариях.
Когда использовать
- Когда есть несколько кандидатов на обработку запроса и заранее неизвестно, кто сработает.
- Когда обработчики должны выстраиваться в цепочку и передавать запрос дальше.
- Когда набор и порядок обработчиков меняется во время выполнения.
- Когда важно разделить ответственность между отдельными обработчиками.
Использование в Laravel
В Laravel Цепочка обязанностей — это HTTP-middleware: Illuminate\Pipeline\Pipeline прогоняет запрос через слой app/Http/Middleware/*, и каждое звено либо продолжает цепочку, либо прерывает её. Тот же конвейер используется в маршрутизаторах и обработке задач, а собранный стек middleware ядра (Illuminate\Foundation\Http\Kernel) — та самая цепочка, по которой проходит каждый запрос.