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