Схема цепочки ответственности
В объектно-ориентированном проектировании шаблон цепочки ответственности представляет собой шаблон поведенческого проектирования, состоящий из источника командных объектов и ряда объектов обработки . [1] Каждый объект обработки содержит логику, определяющую типы командных объектов, которые он может обрабатывать; остальные передаются следующему объекту обработки в цепочке. Также существует механизм добавления новых объектов обработки в конец этой цепочки.
В варианте стандартной модели цепочки ответственности некоторые обработчики могут действовать как диспетчеры , способные отправлять команды в различных направлениях, образуя дерево ответственности . В некоторых случаях это может происходить рекурсивно, когда объекты обработки вызывают объекты обработки более высокого уровня с помощью команд, которые пытаются решить некоторую меньшую часть проблемы; в этом случае рекурсия продолжается до тех пор, пока команда не будет обработана или пока не будет исследовано все дерево. Интерпретатор XML может работать таким же образом.
Этот шаблон продвигает идею слабой связи .
Шаблон цепочки ответственности структурно почти идентичен шаблону декоратора , с той разницей, что для декоратора запрос обрабатывают все классы, а для цепочки ответственности запрос обрабатывает ровно один из классов в цепочке. Это строгое определение концепции ответственности в книге GoF . Однако многие реализации (например, средства ведения журнала ниже, обработка событий пользовательского интерфейса или фильтры сервлетов в Java и т. д.) позволяют нескольким элементам цепочки брать на себя ответственность.
Обзор
[ редактировать ]Цепочка ответственности [2] шаблон проектирования — один из двадцати трех известных Шаблоны проектирования GoF которые описывают общие решения повторяющихся проблем проектирования при разработке гибкого и повторно используемого объектно-ориентированного программного обеспечения, то есть объектов, которые легче реализовать, изменить, протестировать и повторно использовать.
Какие проблемы может решить шаблон проектирования «Цепочка ответственности»?
[ редактировать ]- Следует избегать связи отправителя запроса с его получателем.
- Должна быть возможность, чтобы запрос мог обрабатываться более чем одним получателем.
Реализация запроса непосредственно внутри класса, который отправляет запрос, является негибкой. потому что он связывает класс с конкретным получателем и делает невозможным поддержку нескольких получателей. [3]
Какое решение описывает шаблон проектирования «Цепочка ответственности»?
[ редактировать ]- Определите цепочку объектов-получателей, отвечающих, в зависимости от условий выполнения, либо за обработку запроса, либо за его пересылку следующему получателю в цепочке (если таковой имеется).
Это позволяет нам отправить запрос цепочке получателей. без необходимости знать, какой из них обрабатывает запрос. Запрос передается по цепочке до тех пор, пока получатель не обработает его. Отправитель запроса больше не связан с конкретным получателем.
См. также класс UML и диаграмму последовательности ниже.
Структура
[ редактировать ]Класс UML и диаграмма последовательности
[ редактировать ]
На приведенной выше UML классов диаграмме Sender
class не ссылается непосредственно на конкретный класс получателя.
Вместо, Sender
относится к Handler
интерфейс для обработки запроса ( handler.handleRequest()
), что делает Sender
независимо от того, какой получатель обрабатывает запрос.
Receiver1
, Receiver2
, и Receiver3
классы реализуют Handler
интерфейс путем обработки или пересылки запроса (в зависимости от условий времени выполнения).
Диаграмма UML последовательности
показывает взаимодействие во время выполнения: В этом примере Sender
вызовы объектов handleRequest()
на receiver1
объект (типа Handler
).
receiver1
перенаправляет запрос на receiver2
, который, в свою очередь, перенаправляет запрос на receiver3
, который обрабатывает (выполняет) запрос.
Пример
[ редактировать ]Эта реализация C++11 основана на реализации до C++98, описанной в книге. [5]
#include <iostream>
#include <memory>
typedef int Topic;
constexpr Topic NO_HELP_TOPIC = -1;
// defines an interface for handling requests.
class HelpHandler { // Handler
public:
HelpHandler(HelpHandler* h = nullptr, Topic t = NO_HELP_TOPIC)
: successor(h), topic(t) {}
virtual bool hasHelp() {
return topic != NO_HELP_TOPIC;
}
virtual void setHandler(HelpHandler*, Topic) {}
virtual void handleHelp() {
std::cout << "HelpHandler::handleHelp\n";
// (optional) implements the successor link.
if (successor != nullptr) {
successor->handleHelp();
}
}
virtual ~HelpHandler() = default;
HelpHandler(const HelpHandler&) = delete; // rule of three
HelpHandler& operator=(const HelpHandler&) = delete;
private:
HelpHandler* successor;
Topic topic;
};
class Widget : public HelpHandler {
public:
Widget(const Widget&) = delete; // rule of three
Widget& operator=(const Widget&) = delete;
protected:
Widget(Widget* w, Topic t = NO_HELP_TOPIC)
: HelpHandler(w, t), parent(nullptr) {
parent = w;
}
private:
Widget* parent;
};
// handles requests it is responsible for.
class Button : public Widget { // ConcreteHandler
public:
Button(std::shared_ptr<Widget> h, Topic t = NO_HELP_TOPIC) : Widget(h.get(), t) {}
virtual void handleHelp() {
// if the ConcreteHandler can handle the request, it does so; otherwise it forwards the request to its successor.
std::cout << "Button::handleHelp\n";
if (hasHelp()) {
// handles requests it is responsible for.
} else {
// can access its successor.
HelpHandler::handleHelp();
}
}
};
class Dialog : public Widget { // ConcreteHandler
public:
Dialog(std::shared_ptr<HelpHandler> h, Topic t = NO_HELP_TOPIC) : Widget(nullptr) {
setHandler(h.get(), t);
}
virtual void handleHelp() {
std::cout << "Dialog::handleHelp\n";
// Widget operations that Dialog overrides...
if(hasHelp()) {
// offer help on the dialog
} else {
HelpHandler::handleHelp();
}
}
};
class Application : public HelpHandler {
public:
Application(Topic t) : HelpHandler(nullptr, t) {}
virtual void handleHelp() {
std::cout << "Application::handleHelp\n";
// show a list of help topics
}
};
int main() {
constexpr Topic PRINT_TOPIC = 1;
constexpr Topic PAPER_ORIENTATION_TOPIC = 2;
constexpr Topic APPLICATION_TOPIC = 3;
// The smart pointers prevent memory leaks.
std::shared_ptr<Application> application = std::make_shared<Application>(APPLICATION_TOPIC);
std::shared_ptr<Dialog> dialog = std::make_shared<Dialog>(application, PRINT_TOPIC);
std::shared_ptr<Button> button = std::make_shared<Button>(dialog, PAPER_ORIENTATION_TOPIC);
button->handleHelp();
}
Реализации
[ редактировать ]Какао и какао-прикосновение
[ редактировать ]Платформы Cocoa и Cocoa Touch , используемые для приложений OS X и iOS соответственно, активно используют шаблон цепочки ответственности для обработки событий. Объекты, участвующие в цепочке, называются объектами -ответчиками и наследуются от NSResponder
(ОС Х)/ UIResponder
(iOS) класс. Все объекты просмотра ( NSView
/ UIView
), просмотреть объекты контроллера ( NSViewController
/ UIViewController
), оконные объекты ( NSWindow
/ UIWindow
) и объект приложения ( NSApplication
/ UIApplication
) являются объектами-ответчиками.
Обычно, когда представление получает событие, которое оно не может обработать, оно отправляет его в свое суперпредставление, пока оно не достигнет контроллера представления или объекта окна. Если окно не может обработать событие, оно отправляется объекту приложения, который является последним объектом в цепочке. Например:
- В OS X перемещение текстурированного окна с помощью мыши можно выполнить из любого места (не только из строки заголовка), если только в этом месте нет представления, которое обрабатывает события перетаскивания, например элементы управления ползунком. Если такого представления (или суперпредставления) нет, события перетаскивания отправляются вверх по цепочке в окно, которое обрабатывает событие перетаскивания.
- В iOS обычно события представления обрабатываются в контроллере представления, который управляет иерархией представления, вместо создания подкласса самого представления. Поскольку контроллер представления находится в цепочке ответчиков после всех его управляемых подпредставлений, он может перехватывать любые события представления и обрабатывать их.
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ «Шаблон проектирования цепочки ответственности» . Архивировано из оригинала 27 февраля 2018 г. Проверено 8 ноября 2013 г.
- ^ Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес (1994). Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования . Эддисон Уэсли. стр. 223 и далее . ISBN 0-201-63361-2 .
{{cite book}}
: CS1 maint: несколько имен: список авторов ( ссылка ) - ^ «Шаблон проектирования «Цепочка ответственности: проблема, решение и применимость» . w3sDesign.com . Проверено 12 августа 2017 г.
- ^ «Шаблон проектирования «Цепочка ответственности — структура и сотрудничество» . w3sDesign.com . Проверено 12 августа 2017 г.
- ^ Эрих Гамма (1994). Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования . Эддисон Уэсли. стр. 189 и далее. ISBN 0-201-63361-2 .