Цепочка обязанностей (Chain of Responsibility)

Цепочка обязанностей передаёт запрос по обработчикам. Каждый решает, завершить обработку или вызвать следующий.

Два распространённых режима

  1. Первый подходящий возвращает результат и останавливает цепь.
  2. Конвейер выполняет шаги по порядку до конца или особого условия.

Неясность «кто передаёт дальше» — частый источник пропущенных действий.

Сигналы применения

  • возможных обработчиков несколько, а получатель заранее неизвестен;
  • порядок проверок или преобразований должен задаваться конфигурацией;
  • набор шагов меняется независимо от отправителя;
  • большая условная конструкция последовательно проверяет один запрос.

Роли

  • Handler объявляет обработку;
  • Base Handler хранит ссылку на следующий элемент и делегирование;
  • Concrete Handler распознаёт или обрабатывает запрос;
  • Client собирает цепь и обращается к её началу.

Пример: классификация ошибки сборки

abstract class FailureHandler {
    private final FailureHandler next;
    FailureHandler(FailureHandler next) { this.next = next; }
    public final String classify(String text) {
        String result = tryClassify(text);
        if (result != null) return result;
        return next == null ? "unknown" : next.classify(text);
    }
    protected abstract String tryClassify(String text);
}
final class CompilerFailure extends FailureHandler {
    CompilerFailure(FailureHandler next) { super(next); }
    protected String tryClassify(String text) {
        return text.contains("compilation failed") ? "compiler" : null; }
}
final class DependencyFailure extends FailureHandler {
    DependencyFailure(FailureHandler next) { super(next); }
    protected String tryClassify(String text) {
        return text.contains("could not resolve") ? "dependency" : null; }
}
FailureHandler chain =
        new CompilerFailure(new DependencyFailure(null));
String kind = chain.classify("could not resolve library");

При пересечении признаков специфичные правила ставят раньше общих.

Надёжность цепи

Для необработанного запроса задают fallback или исключение. Полезно наблюдать, какое звено остановило цепь.

В неизменяемой цепи next передают через конструктор. Setter упрощает динамическую перестройку, но требует учитывать конкурентный доступ.

Компромиссы

  • отправитель не зависит от конкретного получателя;
  • порядок и состав можно менять независимо;
  • запрос может остаться необработанным;
  • результат зависит от порядка звеньев;
  • длинная цепь добавляет задержку и усложняет трассировку;
  • общий изменяемый контекст связывает обработчики неявно.

Чем отличается

  • Декоратор Decorator строит объект, обычный вызов проходит через все обёртки;
  • pipeline обычно предполагает все стадии, Chain допускает остановку на получателе;
  • Шаблонный метод Template Method фиксирует порядок наследованием;
  • Observer рассылает событие всем подписчикам, а Chain ищет обработчика по порядку.

Вопросы на собеседовании

  1. Кто формирует цепь? Клиент, фабрика или DI-конфигурация в composition root.
  2. Никто не подошёл? Нужны fallback, специальный результат или исключение.
  3. Почему порядок важен? Несколько обработчиков могут принять один запрос.
  4. Всегда вызывать следующий? Нет, это определяется режимом цепи.
  5. Как тестировать? Отдельно звенья, затем порядок, остановку и fallback. Связи: Поведенческие паттерны, Принципы SOLID, Шаблоны проектирования.