Цепочка обязанностей (Chain of Responsibility)
Цепочка обязанностей передаёт запрос по обработчикам. Каждый решает, завершить обработку или вызвать следующий.
Два распространённых режима
- Первый подходящий возвращает результат и останавливает цепь.
- Конвейер выполняет шаги по порядку до конца или особого условия.
Неясность «кто передаёт дальше» — частый источник пропущенных действий.
Сигналы применения
- возможных обработчиков несколько, а получатель заранее неизвестен;
- порядок проверок или преобразований должен задаваться конфигурацией;
- набор шагов меняется независимо от отправителя;
- большая условная конструкция последовательно проверяет один запрос.
Роли
- 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 ищет обработчика по порядку.
Вопросы на собеседовании
- Кто формирует цепь? Клиент, фабрика или DI-конфигурация в composition root.
- Никто не подошёл? Нужны fallback, специальный результат или исключение.
- Почему порядок важен? Несколько обработчиков могут принять один запрос.
- Всегда вызывать следующий? Нет, это определяется режимом цепи.
- Как тестировать? Отдельно звенья, затем порядок, остановку и fallback. Связи: Поведенческие паттерны, Принципы SOLID, Шаблоны проектирования.