Что такое паттерн "Цепочка обязанностей" (Chain of Responsibility)

Цепочка обязанностей — поведенческий паттерн проектирования, который позволяет передавать запросы последовательно по цепочке обработчиков, пока один из них не обработает запрос. Это избавляет отправителя запроса от знания, какой объект именно выполнит обработку.

Представьте систему, в которой несколько объектов могут обработать определённый запрос, но конкретный обработчик определяется динамически. Каждый объект в цепочке решает, может ли он обработать запрос, и либо обрабатывает его, либо передаёт следующему.

Преимущества паттерна «Цепочка обязанностей»

  • Снижение связанности между отправителем и получателем запроса.
  • Гибкость в распределении обязанностей между объектами.
  • Возможность динамически изменять порядок или состав обработчиков.

Пример:

  • Обработчики: Разные уровни поддержки клиентов (базовый, продвинутый, эксперт).
  • Запросы: Вопросы пользователей различной сложности.

Где применяется паттерн «Цепочка обязанностей»?

  • Системы обработки событий и запросов.
  • Логирование с разными уровнями важности.
  • Обработка исключений и валидация данных.
  • UI-события и middleware.

Пример реализации на JavaScript

class Handler { setNext(handler) { this.nextHandler = handler; return handler; } handle(request) { if (this.nextHandler) { return this.nextHandler.handle(request); } return null; } } class ConcreteHandler1 extends Handler { handle(request) { if (request === "task1") { return "Обработано ConcreteHandler1"; } return super.handle(request); } } class ConcreteHandler2 extends Handler { handle(request) { if (request === "task2") { return "Обработано ConcreteHandler2"; } return super.handle(request); } } // Создание цепочки const handler1 = new ConcreteHandler1(); const handler2 = new ConcreteHandler2(); handler1.setNext(handler2); // Использование console.log(handler1.handle("task1")); // Обработано ConcreteHandler1 console.log(handler1.handle("task2")); // Обработано ConcreteHandler2 console.log(handler1.handle("task3")); // null (не обработано)

Когда стоит использовать паттерн «Цепочка обязанностей»?

  • Нужно избежать жёсткой привязки отправителя запроса к обработчику.
  • Обработчиков может быть несколько, и их порядок или состав могут меняться.
  • Необходимо разделить обработку запроса между разными объектами.

Отличие от других паттернов

  • В отличие от команды, цепочка передаёт запрос по цепочке обработчиков.
  • В отличие от состояния, цепочка не меняет внутреннее состояние объекта, а делегирует обработку другим объектам.

Плюсы паттерна

  • Уменьшение связности компонентов.
  • Гибкое распределение обязанностей.
  • Легко расширять цепочку новыми обработчиками.
  • Удобство в управлении сложными процессами обработки.

Минусы паттерна

  • Запрос может остаться необработанным.
  • Сложно отследить, какой обработчик в итоге обработал запрос.
  • Может увеличить время обработки из-за передачи по цепочке.