Цепочка обязанностей (Chain of Responsibility)
реализации: java, количество: 4
реализации(исходники)
+добавить
Цепочка обязанностей - паттерн поведения, выстраивающий объекты составных частей приложения связанными между собой по цепочке, для передачи запроса на обработку от более низких, детализированных слоев системы к более высоким глобальным. codelab.ru codelab.ru оригинал источник
Условия, Задача, Назначение
Позволяет избежать привязки отправителя запроса к его получателю, давая шанс обработать запрос нескольким объектам. Связывает объекты-получатели в цепочку и передает запрос вдоль этой цепочки, пока его один из получателей его не обработает. codelab.ru codelab.ru оригинал источникВозникает очень гибкая и свободная схема процесса обработки какого-либо запроса: в глобальном объекте какого-нибудь самого верхнего уровня можно определить алгоритм обработки по-умолчанию (в результате чего система уже может быть запущена, только все запросы вне зависимости от их содержания и контекста будут обрабатываться единым способом), а потом лишь там где требуется специальное обслуживание запроса - просто добавлять реализацию методов обработки у соответствующих более низких и детализированных слоев системы.
Мотивация
Рассмотрим контекстно-зависимую оперативную справку в графическом интерфейсе пользователя, который может получить дополнительную информацию по любой части интерфейса, просто щелкнув на ней мышью. Содержание справки зависит от того, какая часть интерфейса и в каком контексте выбрана. Например, справка по кнопке в диалоговом окне может отличаться от справки по аналогичной кнопке в главном окне приложения. Если для некоторой части интерфейса справки нет, то система должна показать информацию о ближайшем контексте, в котором она находится, например, о диалоговом окне в целом. источник оригинал codelab.ru codelab.ru
источник codelab.ru оригинал codelab.ru
источник codelab.ru codelab.ru оригинал
codelab.ru codelab.ru оригинал источник
Признаки применения, использования паттерна Цепочка обязанностей (Chain of Responsibility)
Используйте цепочку обязанностей, когда: codelab.ru оригинал источник codelab.ru- Есть более одного объекта, способного обработать запрос, при этом настоящий обработчик заранее неизвестен и должен быть найден в соответствие с какой-либо кастомной логикой. codelab.ru codelab.ru оригинал источник
- Нужно отправить запрос одному из нескольких объектов, не указывая явно, какому именно. codelab.ru codelab.ru оригинал источник
- Набор объектов, способных обработать запрос, должен определяться динамически. оригинал codelab.ru источник codelab.ru
оригинал источник codelab.ru codelab.ru
оригинал codelab.ru источник codelab.ru
Участники паттерна Цепочка обязанностей (Chain of Responsibility)
оригинал codelab.ru codelab.ru источник- Handler (HelpHandler) – обработчик.
Определяет интерфейс для обработки запросов.
Реализует связь с преемником (необязательно). оригинал codelab.ru источник codelab.ru - ConcreteHandler (PrintButton, PrintDialog) - конкретный обработчик.
Обрабатывает запрос, за который отвечает.
Имеет доступ к своему преемнику.
Если ConcreteHandler способен обработать запрос, то так и делает, если не может, то направляет его - его своему преемнику вверх по цепочке. оригинал codelab.ru источник codelab.ru - Client – клиент.
Отправляет запрос некоторому объекту ConcreteHandler в цепочке. оригинал codelab.ru источник codelab.ru
Схема использования паттерна Цепочка обязанностей (Chain of Responsibility)
Когда клиент инициирует запрос, он начинает продвигаться по цепочке, пока некоторый объект ConcreteHandler не возьмет на себя ответственность за его обработку. codelab.ru источник codelab.ru оригиналВопросы, касающиеся реализации паттерна Цепочка обязанностей (Chain of Responsibility)
При рассмотрении цепочки обязанностей следует обратить внимание на следующие моменты: источник оригинал codelab.ru codelab.ru- Реализация цепочки преемников.
Есть два способа реализовать такую цепочку: определить новые связи (обычно это делается в классе Handler, но можно и в ConcreteHandler), либо использовать существующие.
До сих пор в наших примерах определялись новые связи, однако можно воспользоваться уже имеющимися ссылками на объекты для формирования цепочки преемников. Например, ссылка на родителя в иерархии «часть-целое» (паттерн компоновщик) может заодно определять и преемника «части». В структуре виджетов такие связи тоже могут существовать. В разделе, посвященном паттерну компоновщик, ссылки на родителей обсуждаются более подробно.
Существующие связи можно использовать, когда они уже поддерживают нужную цепочку. Тогда мы избежим явного определения новых связей и сэкономим память. Но если структура не отражает устройства цепочки обязанностей, то уйти от определения избыточных связей не удастся. codelab.ru codelab.ru оригинал источник - Соединение преемников.
Если готовых ссылок, пригодных для определения цепочки, нет, то их придется ввести. В таком случае класс Handler не только определяет интерфейс запросов, но еще и хранит ссылку на преемника. Следовательно у обработчика появляется возможность определить реализацию операции HandleRequest по умолчанию - перенаправление запроса преемнику (если таковой существует). Если подкласс ConcreteHandler не заинтересован в запросе, то ему и не надо замещать эту операцию, поскольку по умолчанию запрос как раз и отправляется дальше. Вот пример базового класса HelpHandler, в котором хранится указатель на преемника:оригинал codelab.ru источник codelab.ru- package chainOfResponsibility;
- public class HelpHandler {
- HelpHandler successor;
- public HelpHandler(HelpHandler successor) {
- super();
- this.successor = successor;
- }
- public void HandleHelp() {
- if (successor != null) {
- successor.HandleHelp();
- }
- }
- }
- Представление запросов.
Представлять запросы можно по-разному. В простейшей форме, например в случае класса HandleHelp, запрос жестко кодируется как вызов соответствующего дополнительного метода этого класса. Это удобно и безопасно, но переадресовывать тогда можно только фиксированный набор запросов, для которых в классе Handler определены специальные методы.
Альтернатива - использовать единую функцию-обработчик, которой передается код запроса (скажем, целое число или строка). Так можно поддержать заранее неизвестное число запросов. Единственное требование состоит в том, что отправитель и получатель должны договориться о способе кодирования запроса.
Это более гибкий подход, но при реализации нужно использовать условные операторы для распределения запросов по коду их обработки. Кроме того, не существует безопасного с точки зрения типов способа передачи параметров, поэтому упаковывать и распаковывать их приходится вручную. Очевидно, что это не так безопасно, как прямой вызов операции.
Чтобы решить проблему передачи параметров, допустимо использовать отдельные объекты-запросы, в которых инкапсулированы параметры запроса. Т.е. специальный класс Request, например, может представлять некоторые запросы явно, а их новые типы описываются в подклассах. Подкласс может определить другие параметры. Обработчик должен иметь информацию о типе запроса (какой именно подкласс Request используется), чтобы разобрать эти параметры.
Для идентификации запроса в классе Request можно определить функцию доступа, которая возвращает идентификатор класса. Вместо этого получатель мог бы воспользоваться информацией о типе, доступной во время выполнения, если язык программирования поддерживает такую возможность (в случае Java-ы для этих целей можно б было использовать результат выполнения .getClass().getName()).
Приведем пример функции диспетчеризации, в которой используются объекты для идентификации запросов. Операция GetKind, указанная в базовом классе Request, определяет вид запроса:- package chainOfResponsibility;
- switch (req.GetKind()) {
- HandleHelp(req);
- break;
- HandlePrint(req);
- break;
- default:
- // ...
- break;
- }
- }
- // ...
- }
- // ... }
- }
И подклассы могут расширить схему диспетчеризации, переопределив операцию HandleRequest. Подкласс обрабатывает лишь те запросы, в которых заинтересован, а остальные отправляет родительскому классу. В этом случае подкласс именно расширяет, а не замещает операцию HandleRequest. Например, подкласс ExtendedHandler расширяет операцию HandleRequest, определенную в классе Handler, следующим образом:оригинал codelab.ru codelab.ru источник- package chainOfResponsibility;
- switch (req.GetKind()) {
- HandlerPreview(req);
- break;
- default:
- super.HandleHelp(req);
- break;
- }
- }
- // ...
- }
- }
- Автоматическое перенаправление запросов специфическими средствами языка.
Такими как, например, анонимная функция __call() в PHP, начиная с 5-ой версии, автоматически вызываемая средой языка если происходит попытка вызвать у объекта несуществующий в нем метод.
В Smalltalk для этих целей используется механизм doesNotUnderstand.
В любом случае мы получаем функционал, при котором сообщения, не имеющие соответствующих методов, перехватываются реализацией __call(), doesNotUnderstand, которая может быть замещена для перенаправления сообщения объекту-преемнику. Поэтому осуществлять перенаправление вручную уже не придется.
Класс обрабатывает только запросы, в которых заинтересован, и ожидает, что механизм doesNotUnderstand()/__call() выполнит обработку по-умолчанию для всех остальных запросов. codelab.ru оригинал источник codelab.ru
Результаты
Паттерн цепочка обязанностей имеет следующие достоинства и недостатки: оригинал codelab.ru codelab.ru источник- Ослабление связанности.
Этот паттерн освобождает объект от необходимости «знать», кто конкретно обработает его запрос. Отправителю и получателю ничего неизвестно друг о друге, а включенному в цепочку объекту - о структуре цепочки.
Таким образом, цепочка обязанностей помогает упростить взаимосвязи между объектами. Вместо того чтобы хранить ссылки на все объекты, которые могут стать получателями запроса, объект должен располагать информацией лишь о своем ближайшем преемнике. источник оригинал codelab.ru codelab.ru - Дополнительная гибкость при распределении обязанностей между объектами.
Цепочка обязанностей позволяет повысить гибкость распределения обязанностей между объектами. Добавить или изменить обязанности по обработке запроса можно, включив в цепочку новых участников или изменив ее каким-то другим образом. Этот подход можно сочетать со статическим порождением подклассов для создания специализированных обработчиков. codelab.ru источник codelab.ru оригинал - Получение не гарантировано.
Поскольку у запроса нет явного получателя, то нет и гарантий, что он вообще будет обработан: он может достичь конца цепочки и пропасть. Необработанным запрос может оказаться и в случае неправильной конфигурации цепочки. оригинал codelab.ru codelab.ru источник
Пример
В языке программирования Java использование Паттерн цепочка обязанностей самым непосредственным образом включено в ядро языка. Я, например, понял это в следующий момент, после того, когда подумал о каком-нибудь примере этого паттерна на java. Если еще не догадались, то речь идет о системе обработки исключений языка, т.е. exception-ов. Как известно, там имеется специальный тип исключений – объявляемые исключения (checked exceptions). Кратко: если какой-то метод объявляет такое исключение, то любой другой метод, который его вызывает должен либо перехватить и обработать это исключение, либо объявить его в своей сигнатуре, таким образом, передавая эту обязанность вверх по цепочке вызовов. Т.е здесь имеем, что в качестве запроса выступает исключение (ошибка, исключительная ситуация времени выполнения), которое может выброситься любым участком кода во время выполнения программы. Выражаясь терминами паттерна, этим отсылается запрос на обработку этого исключения вверх по всей иерархии вызовов этого метода. Наиболее ответственный методов в этой цепочке вызовов и обрабатывает эту ошибку, прекращая ее следование вверх по иерархии, в противном случае если она не будет перехвачена – в конце концов вызывается специальный метод, по-умолчанию, печатающий стек-трейс ошибки в консоль. codelab.ru источник оригинал codelab.ruУ новости есть заголовок, текст и собственно дата новости: News. codelab.ru оригинал источник codelab.ru
Новости хранятся в базе данных и для того, чтобы их оттуда доставать любые модули, совершенно ничего не знающие ни о каких базах, определена сущность DAO (Data Access Object), у которой имеются 2 необходимых метода: достать одну последнюю новость или список всех новостей: DAO_I. codelab.ru источник codelab.ru оригинал
codelab.ru codelab.ru оригинал источник
Известные применения паттерна Цепочка обязанностей (Chain of Responsibility)
Паттерн цепочка обязанностей используется в нескольких библиотеках классов для обработки событий, инициированных пользователем. Класс Handler в них называется по-разному, но идея всегда одна и та же: когда пользователь щелкает кнопкой мыши или нажимает клавишу, генерируется некоторое событие, которое распространяется по цепочке. В МасАрр и ЕТ++ класс называется EventHandler, в библиотеке TCL[google] фирмы Symantec - Bureaucrat, а в библиотеке из системы [google=NeXT Responder]NeXT – Responder. codelab.ru источник оригинал codelab.ruРодственные паттерны
Паттерн цепочка обязанностей часто применяется вместе с паттерном компоновщик. В этом случае родитель компонента может выступать в роли его преемника. оригинал codelab.ru источник codelab.ruРеализации: java(4) +добавить реализацию
1) News.java, code #483[автор:this]
2) DAO_I.java, code #484[автор:this]
3) DAO.java, code #485[автор:this]
4) NewsPage.java, code #486[автор:this]



