Коротка відповідь
middleChain of Responsibility — це патерн, що передає запит через ланцюжок обробників до тих пір, поки один з них не його спрацює. Кожен обробник має посилання на наступного, а також перевіряє умову. Приклад: `handler.next = nextHandler; if (canHandle(request)) { handle(request); } else { handler.next.handle(request); }`. Це дозволяє розділити логіку обробки і легко додавати нові кроки.
Повне пояснення
1. Що це і навіщо
Chain of Responsibility (CoR) – патерн поведінки, що дозволяє передавати запит через ланцюжок об’єктів‑обробників, поки один з них не прийме рішення. Використовується, коли кілька об’єктів можуть обробити запит, але конкретний вибір залежить від стану або контексту.
2. Принцип роботи / алгоритм
Кожен обробник реалізує інтерфейс Handler з методом handle(request) і має поле next: Handler | null. У методі перевіряємо умову (canHandle(request)); якщо так – обробляємо, інакше передаємо next.handle(request). Ланцюжок формується послідовно: handlerA.next = handlerB; handlerB.next = handlerC;.
3. Реалізація (JS / TS)
- Базова реалізація
interface Handler {
next?: Handler;
handle(request: string): void;
}
class ConcreteHandlerA implements Handler {
next?: Handler;
handle(request: string) {
if (request === 'A') console.log('Handled by A');
else this.next?.handle(request);
}
}
- Веб‑dev приклад – логін‑система, де перший обробник перевіряє токен, другий – пароль, третій – 2FA.
class TokenHandler implements Handler { /* ... */ }
class PasswordHandler implements Handler { /* ... */ }
class TwoFAHandler implements Handler { /* ... */ }
const chain = new TokenHandler();
chain.next = new PasswordHandler();
chain.next.next = new TwoFAHandler();
- Новачковий приклад – обробка команд у чат‑боті:
HelpHandler,EchoHandler,UnknownHandler.
class HelpHandler implements Handler { /* ... */ }
4. Тестування (Jest або Vitest)
test('CoR handles request by first capable handler', () => {
const a = new ConcreteHandlerA();
const b = { handle: jest.fn() } as Handler;
a.next = b;
a.handle('A');
expect(b.handle).not.toHaveBeenCalled();
});
5. Проблеми та edge cases
- Неправильне використання ролей – обробник, що не виконує
handle, може зламати ланцюжок. - Зайва абстракція – надмірна кількість обробників ускладнює розуміння.
- Cyclical dependencies – цикл в ланцюжку викликає рекурсивний стек.
- Плутанина між подібними паттернами – CoR vs Strategy: перший передає запит, другий вибирає алгоритм.
- Performance‑пастки – довгий ланцюжок збільшує час виконання, особливо при синхронних обробках.
6. Оптимізація та продуктивність
- Кожен обробник виконує лише одну перевірку; якщо ланцюжок короткий, затримка мінімальна.
- Для великих систем можна кешувати результат
canHandleабо використовувати дерево пріоритетів. - Пам’ять: лише посилання на наступного, тому накладні витрати мінімальні.
7. Без JS / архітектурні альтернативи
- У чистій HTML/CSS можна реалізувати «цепочку» через каскадні стилі:
:hover→:focus-within→:active. - У серверних мовах без патерна можна просто послідовно викликати функції, передаючи результат у наступну.
8. Cheatsheet + follow-up
- Шпаргалка:
Handler → next? → handle(request); перевірка → обробка → передача. - Питання на інтерв’ю: 1) Як уникнути циклічних залежностей у CoR? 2) Коли варто обирати CoR замість Strategy?