Chain of Responsibility (Цепочка обязанностей)
Проблема
При тестировании REST API каждый HTTP-ответ требует набора стандартных проверок: залогировать запрос и ответ, проверить статус-код, при 5xx — повторить запрос (retry), при ошибке — добавить диагностическую информацию. Без структуры вся эта логика собирается в один перегруженный if-else блок прямо в тестовом хелпере. Добавление нового шага обработки (например, маскирование чувствительных данных в логах) требует правки в середине этого монолита.
Chain of Responsibility выстраивает обработчики в цепочку. Каждый обработчик решает, обработать ли запрос самостоятельно и передать ли его дальше по цепочке. Новый шаг добавляется как отдельный класс и вставляется в нужное место цепочки без правки существующих обработчиков.
Структура модуля
chain-of-responsibility/
├── build.gradle
└── src/
├── main/
│ └── java/
│ └── dev/shipovalov/chain/
│ ├── ApiResponse.java # Модель HTTP-ответа
│ ├── ResponseHandler.java # Абстрактный обработчик с ссылкой на следующий
│ └── handlers/
│ ├── LoggingHandler.java # Шаг 1: логировать статус и тело ответа
│ ├── StatusCodeHandler.java # Шаг 2: бросить исключение при 4xx/5xx
│ └── RetryHandler.java # Шаг 3: повторить запрос при 5xx
└── test/
└── java/
└── dev/shipovalov/chain/
└── ChainOfResponsibilityTest.java # Тест с различными конфигурациями цепочки
Ключевые классы
| Класс | Назначение |
|---|---|
ResponseHandler |
Абстрактный класс с полем next, методом then(next) для сборки цепочки и passToNext() для передачи управления |
LoggingHandler |
Логирует статус-код и URL — всегда передаёт дальше |
StatusCodeHandler |
При 5xx бросает RuntimeException с деталями ответа |
RetryHandler |
При 429/503 увеличивает счётчик повторных попыток (retry) |
ApiResponse |
Record: (int statusCode, String body, String url)
|
Пример использования
// Сборка цепочки через fluent-метод then()
ResponseHandler chain = new LoggingHandler();
chain.then(new StatusCodeHandler())
.then(new RetryHandler(3));
// Использование цепочки
ApiResponse response = new ApiResponse(200, "{\"ok\":true}", "/api/users/1");
chain.handle(response); // пройдёт через все обработчики
// Добавление нового шага без правки существующих обработчиков
ResponseHandler extendedChain = new LoggingHandler();
extendedChain.then(new StatusCodeHandler())
.then(new RetryHandler(5));
Запуск тестов
./gradlew :chain-of-responsibility:test
Отчёт о результатах: chain-of-responsibility/build/reports/tests/test/index.html