Шаблон Цепочка ответственностей предназначен для предотвращения связывания инициатора сообщения с конкретным экземпляром его обработчика. При этом само сообщение передается по цепочке, в которой каждое звено выбирает между обработкой и пересылкой данных дальше.
Цепочка ответственностей применяется в случаях, если:
- существует более одного обработчика исходного запроса;
- (или) обработчики сообщения определяются во время выполнения приложения;
- (или) необходимо отправить сообщение без явного указания получателя.
Как можно понять из описания, использование данного шаблона приводит к организации обработчиков событий в виде древовидной структуры или последовательной цепи. При этом само сообщение передается от более низких (детализированных) слоев к более высоким (более абстрактным).
Рассмотрим в качестве примера событие "нажатие клавиши", когда в приложении открыто диалоговое окно. Сообщение будет передано самому контекстному обработчику, принадлежащего активному элементу управления. Далее, в случае необходимости, оно будет переслано к более абстрактным обработчикам, принадлежащим (в порядке очередности) диалогу, его родительскому окну, а затем приложению. И если ни один из них не отреагирует на это сообщение, то оно будет утеряно.
Еще одним примером может служить обработка исключений. Сообщение проходит по цепи обработчиков, представляющих в древовидную структуру, начиная с локальных и заканчивая уровнем приложения. Последний, в отличии от предыдущего примера, как правило реагирует на любое полученное сообщение и прерывает работу самого приложения.
В результате применения шаблона:
- Уменьшается связанность, т.к. отправителю и получателю нет необходимости знать обо всех обработчиках в цепочке, а так же какие именно их них отреагируют на запрос. Более того, сами обработчики могут не знать друг о друге и даже о следующем звене. Наглядным примером может служить обработка исключений: метод, выбросивший его, ничего не знает о том где и как оно будет обработано.
- Увеличивается гибкость приложения, т.к. сама цепочка обработчиков может меняться независимо от клиента. Последнему необходимо знать только точку для отправки сообщения.
Можно выделить следующих участников шаблона:
- Интерфейс обработчика (IHandler) – определяет общий интерфейс для взаимодействия с клиентами.
- Обработчик (Handler) – непосредственная реализация, которая обрабатывает запрос.
- Управляющий объект (Manager) – не обязательный участник, который используется для построения структуры обработчиков и управления ей.
Особенности реализации и применения шаблона
При реализации Цепочки ответственностей необходимо учитывать тот факт, что сообщение может быть не обработано вовсе. Это возможно в том случае, если ни один из участников цепочки не будет знать что с ним делать. К такому же результату приводит неправильная конфигурация последовательности или ошибки при ее построении.
Стоит отметить, что обработка сообщения не означает обязательного отказа в передаче его следующему обработчику в цепочке.
Для создания структуры обработчиков часто применяют шаблон Компоновщик. При этом ссылки на родительский объект могут храниться как в них самих, тaк и отдельно. В последнем случае пересылкой сообщений занимается дополнительный управляющий объект, а обработчики освобождаются от необходимости знать свой родительский узел. Они возвращают одно из установленных значений, чтобы показать было ли обработано сообщение. В зависимости от него управляющий объект принимает решение передавать сообщение дальше или завершить процесс обработки.
Еще одним важным моментом является определение формата сообщения. Возможны варианты:
- Сообщением является экземпляр определенного класса. В исходном коде приложения это будет выглядеть как обычный вызов метода с параметрами. Это удобно и достаточно безопасно, т.к. компилятор позволит подставить только заданные типы параметров. Разработчику остается проконтролировать чтобы они были в диапазоне допустимых значений. Однако, для добавления нового сообщения потребуется создание нового метода. Это возможно приведет к изменению интерфейса и модификации классов всех существующих обработчиков.
- Сообщение передается в виде кода. Например, это как число или текстовая строка заданного формата. В этом случае появляется возможность добавлять новые типы сообщений без изменения интерфейса. Но при этом необходимо контролировать получаемые от клиентов сообщения, т.к. формат может быть нарушен как по ошибке, так и намеренно.
Реализация шаблона в общем виде
- разрабатываем интерфейс обработчиков;
- определяем схему построения цепочки или дерева обработчиков (от контекстных к абстрактным);
- выбираем формат сообщений;
- определяем точку или способ взаимодействия с клиентом;
- клиент отправляет сообщения, которое игнорируется или обрабатывается получателями;
Пример реализации
В примере рассмотрим как разные уровни строительства дома возлагаются на разные профессии.
Создадим интерфейс IWorker.
Создадим абстрактный класс рабочего, реализующий интерфейс.
Создадим конкретного рабочего, начнем с Дизайнера, который создаст дизайн дома.
По аналогии создадим других рабочих.
Рассмотрим применение паттерна.
Результат.