Шаблоны проектирования очень важны для веб-разработчиков, поскольку позволяют повышать качество кода. В этой статье будет представлен шаблон “Цепочка ответственности” с использованием TypeScript.
Шаблон “Цепочка ответственности” (“Цепочка обязанностей”) — это способ избежать жесткой зависимости между отправителем и получателем запроса. При этом нескольким объектам предоставляется возможность обрабатывать запрос. В шаблоне “Цепочка ответственности” множество объектов образуют цепочку, в которой каждый объект связан с последующим ссылкой. Запросы передаются по цепочке до тех пор, пока один из объектов в цепочке не решит обработать запрос.
У каждой должности в компании свои обязанности и полномочия. Возьмем в качестве примера процесс взятия отгулов в компании: когда я прошу отгул, он должен быть одобрен только руководителем группы, и нет необходимости передавать его супервайзеру и директору. Если звено в цепочке ответственности не может обработать текущий запрос, то при наличии следующего звена запрос будет передан на обработку этому следующему звену.
В процессе разработки ПО распространенным сценарием применения шаблона “Цепочка ответственности” становится промежуточное программное обеспечение. Посмотрим, как используется шаблон “Цепочка ответственности” для обработки запросов.
Чтобы лучше понять код ниже, взгляните сначала на соответствующую UML-диаграмму:
На приведенном выше изображении определен интерфейс Handler
. В этом интерфейсе определены следующие два метода.
use(h: Handler): Handler
=> используется для регистрации обработчика (промежуточного ПО)get(url: string, callback: (data: any) => void): void
=> регистрация обработчика запросовget
.
Интерфейс Handler
:
interface Handler {
use(h: Handler): Handler;
get(url: string, callback: (data: any) => void): void;
}
Теперь определим абстрактный класс AbstractHandler
, который инкапсулирует логику обработки цепочки ответственности. То есть объединим различные обработчики, чтобы сформировать ссылочную цепочку.
Абстрактный класс AbstractHandler
:
abstract class AbstractHandler implements Handler { next!: Handler; use(h: Handler) { this.next = h; return this.next; } get(url: string, callback: (data: any) => void) { if (this.next) { return this.next.get(url, callback); } } }
Основываясь на абстрактном классе AbstractHandler
, определяем AuthMiddleware
и LoggerMidddleware
соответственно. Промежуточное ПО AuthMiddleware
используется для обработки аутентификации пользователя, а LoggerMidddleware
— для вывода журналов запросов.
Класс AuthMiddleware
:
class AuthMiddleware extends AbstractHandler { isAuthenticated: boolean; constructor(username: string, password: string) { super(); this.isAuthenticated = false; if (username === "bytefer" && password === "666") { this.isAuthenticated = true; } } get(url: string, callback: (data: any) => void) { if (this.isAuthenticated) { return super.get(url, callback); } else { throw new Error("Not Authorized"); } } }
Класс LoggerMiddleware
:
class LoggerMiddleware extends AbstractHandler {
get(url: string, callback: (data: any) => void) {
console.log(`Request url is: ${url}`);
return super.get(url, callback);
}
}
С помощью промежуточного ПО AuthMiddleware
и LoggerMidddleware
определим класс Route
для регистрации этих промежуточных программ.
Класс Route
:
class Route extends AbstractHandler { urlDataMap: { [key: string]: any }; constructor() { super(); this.urlDataMap = { "/api/todos": [ { title: "Learn Design Pattern" }, ], "/api/random": () => Math.random(), }; } get(url: string, callback: (data: any) => void) { super.get(url, callback); if (this.urlDataMap.hasOwnProperty(url)) { const value = this.urlDataMap[url]; const result = typeof value === "function" ? value() : value; callback(result); } } }
После определения класса Route
можно использовать его и зарегистрировать промежуточные программы следующим образом:
const route = new Route(); route.use(new AuthMiddleware("bytefer", "666")) .use(new LoggerMiddleware()); route.get("/api/todos", (data) => { console.log(JSON.stringify({ data }, null, 2)); }); route.get("/api/random", (data) => { console.log(data); });
При успешном запуске приведенного выше кода вывод будет таким, как на следующем изображении:
Подведем итого того, как используется шаблон “Цепочка ответственности”.
- Необходимо отправить запрос одному из нескольких объектов без явного указания получателя.
- Имеется несколько объектов, которые могут обрабатывать запрос, и какой из них обработает запрос, определяется автоматически во время выполнения, а клиенту нужно только отправить запрос в цепочку.
Читайте также:
- Настройка проекта TypeScript с помощью ESLint, Prettier и VS Code
- Как улучшить код на TypeScript: 5 рекомендаций
- Как улучшить производительность Jest в средах CI с использованием TypeScript
Читайте нас в Telegram, VK и Дзен
Перевод статьи Bytefer: Design Patterns: Chain of Responsibility Pattern in TypeScript