Паттерн Chain of Responsibility – это поведенческий паттерн, который создает цепочку обработчиков запросов, позволяя каждому из них обработать запрос либо передать его следующему обработчику в цепи. Структура паттерна включает в себя абстрактный класс Handler, определяющий интерфейс для обработчиков, конкретные обработчики (ConcreteHandler), реализующие этот интерфейс, и клиента (Client), инициирующего запрос [3, с. 217].
В центре паттерна лежит принцип разделения ответственности между обработчиками, что способствует легкости изменения и масштабирования системы. Когда запрос поступает, он проходит по цепочке обработчиков, пока не будет успешно обработан или не достигнет конца цепи. Это обеспечивает гибкость, так как можно добавлять, удалять или изменять обработчики без изменения клиентского кода [2, с. 157].
Паттерн находит широкое применение в ситуациях, где необходимо обеспечить различные уровни обработки запросов с возможностью динамической модификации цепочки обработчиков. Среди примеров использования - обработка событий в пользовательских интерфейсах, системы фильтрации или валидации данных, где каждый обработчик представляет определенное правило или операцию.
Структура паттерна представлена ниже (рис. 1).
Рис. 1. Структура паттерна Chain of Responsibility
В разработке форм и механизмов валидации данных может быть применен для обработки ввода пользователя. Каждый обработчик в цепочке может проверять определенные аспекты данных, такие как формат, допустимые символы, длина и т. д., обеспечивая тем самым гибкость в определении правил валидации.
В веб-приложениях, где эффективная обработка HTTP-запросов является критически важной частью функционала, паттерн Chain of Responsibility может успешно применяться для управления различными этапами обработки запросов. Это позволяет эффективно управлять последовательностью операций, не загромождая код основного приложения.
Первый обработчик в цепочке может быть ответственен за аутентификацию пользователя. Если запрос содержит информацию для аутентификации, этот обработчик попытается верифицировать пользователя, используя различные методы, такие как проверка логина-пароля, токена или других аутентификационных механизмов.
Следующий обработчик может обеспечивать проверку прав доступа пользователя, основываясь на его роли или других параметрах. Задачей этого этапа может быть убеждение, что пользователь имеет права на выполнение запрашиваемой операции.
Дополнительные обработчики могут быть добавлены для маршрутизации запросов на различные части приложения в зависимости от URL, параметров запроса или других факторов. Это может обеспечить модульность и расширяемость приложения, позволяя динамически изменять маршруты без изменения основной логики.
Завершающий обработчик может выполнять конечную логику приложения, связанную с выполнением запроса. Это может включать в себя вызов контроллера, сервиса или другого компонента, ответственного за обработку конкретного запроса.
Далее будет приведен пример реализации данного паттерна на языке программирования TypeScript.
Начнем с основного интерфейса Handler (рис. 2).
Рис. 2. Основной интерфейс, который будут наследовать остальные обработчики
Метод setNext (handler: Handler): Handler – для назначения следующего обработчика, а также метод handleRequest(request: HttpRequest): void, который принимает наш запрос.
Далее напишем первый обработчик, который ответственный за аутентификацию пользователя. Данный класс будет отвечать за проверку успешной аутентификации пользователя (рис. 3).
Рис. 3. Класс AuthenticationHandler
Следующим будет обработчик, обеспечивающий проверку прав доступа у запрашиваемого пользователя (рис. 4).
Рис. 4. Класс AuthorizationHandler
Этот пример демонстрирует создание обработчиков для аутентификации и авторизации. В данном случае запросы на аутентификацию передаются от одного обработчика к другому в цепочке. Важно отметить, что обработчики можно легко добавлять или изменять, обеспечивая гибкость системы обработки HTTP-запросов.
Произведем оценку производительности паттерна по следующим параметрам: время выполнения, память, сложность алгоритма [3, с. 222].
Время выполнения: Паттерн влечет за собой небольшую дополнительную накладную из-за передачи запроса по цепочке обработчиков. Однако, влияние на время выполнения обычно невелико, и зависит от числа обработчиков в цепочке.
Память: Использование памяти для каждого обработчика может оказаться неэффективным в случае большого числа обработчиков в цепочке. В целом, структура цепочки может занимать дополнительную память.
Сложность алгоритма: Паттерн может усложнить алгоритм обработки запросов в сравнении с прямым вызовом обработчиков. Это особенно актуально в случае, когда каждый обработчик выполняет сложные вычисления.
В системах обработки запросов данный паттерн очень хорошо показывает границы между слоями проверок, который проходит запрос, что повышает модульность и чистота кода, что обеспечивает легкость вносимых изменений в отдельные аспекты проверки, не затрагивая другие. Это способствует их гибкости и поддерживаемости в течение времени.