Используйте цепочку обязанностей, когда:
• Есть более одного объекта, способного обработать запрос, причем настоящий обработчик заранее неизвестен и должен быть найден автоматически.
• Вы хотите отправить запрос одному из нескольких объектов, не указывая явно, какому именно.
• Набор объектов, способных обработать запрос, должен задаваться динамически.
Chain of Responsibility: структура
Рисунок 28
Участники
• Handler (HelpHandler) – обработчик - определяет интерфейс для обработки запросов и (необязательно) реализует связь с преемником;
• ConcreteHandler (PrintButton, PrintDialog) - конкретный обработчик - обрабатывает запрос, за который отвечает;
- имеет доступ к своему преемнику;
- если ConcreteHandler способен обработать запрос, то так и делает, если не может, то направляет его - его своему преемнику;
• Client – клиент - отправляет запрос некоторому объекту ConcreteHandler в цепочке.
Отношения
Когда клиент инициирует запрос, он продвигается по цепочке, пока некоторый объект ConcreteHandler не возьмет на себя ответственность за его обработку.
Реализация
1) Реализация цепочки преемников. Есть два способа реализовать такую цепочку: определить новые связи (обычно это делается в классе Handler, но можно и в ConcreteHandler) или использовать существующие связи. До сих пор в наших примерах определялись новые связи, однако можно воспользоваться уже имеющимися ссылками на объекты для формирования цепочки преемников. Например, ссылка на родителя в иерархии «часть-целое» может заодно определять и преемника «части». В структуре виджетов такие связи тоже могут существовать. Существующие связи можно использовать, когда они уже поддерживают нужную цепочку. Тогда мы избежим явного определения новых связей и сэкономим память. Но если структура не отражает устройства цепочки обязанностей, то уйти от определения избыточных связей не удастся;
2) Соединение преемников. Если готовых ссылок, пригодных для определения цепочки, нет, то их придется ввести. В таком случае класс Handler не только определяет интерфейс запросов, но еще и хранит ссылку на преемника. Следовательно у обработчика появляется возможность определить реализацию операции HandleRequest по умолчанию - перенаправление запроса преемнику (если таковой существует). Если подкласс ConcreteHandler не заинтересован в запросе, то ему и не надо замещать эту операцию, поскольку по умолчанию запрос как раз и отправляется дальше.
3) Представление запросов. Представлять запросы можно по-разному. В простейшей форме, например в случае класса HandleHelp, запрос жестко кодируется как вызов некоторой операции. Это удобно и безопасно, но переадресовывать тогда можно только фиксированный набор запросов, определенных в классе Handler. Альтернатива - использовать одну функцию-обработчик, которой передается код запроса (скажем, целое число или строка). Так можно поддержать заранее неизвестное число запросов. Единственное требование состоит в том, что отправитель и получатель должны договориться о способе кодирования запроса. Это более гибкий подход, но при реализации нужно использовать условные операторы для раздачи запросов по их коду. Кроме того, не существует безопасного с точки зрения типов способа передачи параметров, поэтому упаковывать и распаковывать их приходится вручную. Очевидно, что это не так безопасно, как прямой вызов операции. Чтобы решить проблему передачи параметров, допустимо использовать отдельные объекты-запросы, в которых инкапсулированы параметры запроса. Класс Request может представлять некоторые запросы явно, а их новые типы описываются в подклассах. Подкласс может определить другие параметры. Обработчик должен иметь информацию о типе запроса (какой именно подкласс Request используется), чтобы разобрать эти параметры. Для идентификации запроса в классе Request можно определить функцию доступа, которая возвращает идентификатор класса. Вместо этого получатель мог бы воспользоваться информацией о типе, доступной во время выполнения, если язык программирования поддерживает такую возможность.
Применение в Java API
В версии 1.0 языка Java использовался шаблон Chain of Responsibility для обработки событий пользовательского интерфейса. При этом в качестве цепочки применялась иерархия контейнеров пользовательского интерфейса. Если событие отправлялось кнопке или другому элементу GUI, он должен был или обработать событие, или передать его своему контейнеру. Хотя эта схема была работоспособной, существовало достаточно проблем, поэтому создатели языка Java предприняли решительные шаги по изменению модели событий в языке Java.
Паттерн Command.
Дата: 2019-02-25, просмотров: 238.