Inobject-oriented design, thechain-of-responsibility pattern is abehavioraldesign pattern consisting of a source ofcommand objects and a series ofprocessing objects.[1] Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain. A mechanism also exists for adding new processing objects to the end of this chain.
In a variation of the standard chain-of-responsibility model, some handlers may act asdispatchers, capable of sending commands out in a variety of directions, forming atree of responsibility. In some cases, this can occur recursively, with processing objects calling higher-up processing objects with commands that attempt to solve some smaller part of the problem; in this case recursion continues until the command is processed, or the entire tree has been explored. AnXMLinterpreter might work in this manner.
This pattern promotes the idea ofloose coupling.
The chain-of-responsibility pattern is structurally nearly identical to thedecorator pattern, the difference being that for the decorator, all classes handle the request, while for the chain of responsibility, exactly one of the classes in the chain handles the request. This is a strict definition of the Responsibility concept in theGoF book. However, many implementations (such as loggers below, or UI event handling, or servlet filters in Java, etc.) allow several elements in the chain to take responsibility.
The Chain of Responsibility[2]design pattern is one of the twenty-three well-knownGoF design patterns that describe common solutions to recurring design problems when designing flexible and reusable object-oriented software, that is, objects that are easier to implement, change, test, and reuse.
Implementing a request directly within the class that sends the request is inflexiblebecause it couples the class to a particular receiver and makes it impossible to support multiple receivers.[3]
This enables us to send a request to a chain of receiverswithout having to know which one handles the request.The request gets passed along the chain until a receiver handles the request.The sender of a request is no longer coupled to a particular receiver.
See also the UML class and sequence diagram below.
In the aboveUMLclass diagram, theSender
class doesn't refer to a particular receiver class directly.Instead,Sender
refers to theHandler
interface for handling a request (handler.handleRequest()
), which makes theSender
independent of which receiver handles the request.TheReceiver1
,Receiver2
, andReceiver3
classes implement theHandler
interface by either handling or forwarding a request (depending on run-time conditions).
TheUMLsequence diagramshows the run-time interactions: In this example, theSender
object callshandleRequest()
on thereceiver1
object (of typeHandler
). Thereceiver1
forwards the request toreceiver2
, which in turn forwards the request toreceiver3
, which handles (performs) the request.
This C++11 implementation is based on the pre C++98 implementation in the book.[5]
#include<iostream>#include<memory>typedefintTopic;constexprTopicNO_HELP_TOPIC=-1;// defines an interface for handling requests.classHelpHandler{// Handlerpublic:HelpHandler(HelpHandler*h=nullptr,Topict=NO_HELP_TOPIC):successor(h),topic(t){}virtualboolhasHelp(){returntopic!=NO_HELP_TOPIC;}virtualvoidsetHandler(HelpHandler*,Topic){}virtualvoidhandleHelp(){std::cout<<"HelpHandler::handleHelp\n";// (optional) implements the successor link.if(successor!=nullptr){successor->handleHelp();}}virtual~HelpHandler()=default;HelpHandler(constHelpHandler&)=delete;// rule of threeHelpHandler&operator=(constHelpHandler&)=delete;private:HelpHandler*successor;Topictopic;};classWidget:publicHelpHandler{public:Widget(constWidget&)=delete;// rule of threeWidget&operator=(constWidget&)=delete;protected:Widget(Widget*w,Topict=NO_HELP_TOPIC):HelpHandler(w,t),parent(nullptr){parent=w;}private:Widget*parent;};// handles requests it is responsible for.classButton:publicWidget{// ConcreteHandlerpublic:Button(std::shared_ptr<Widget>h,Topict=NO_HELP_TOPIC):Widget(h.get(),t){}virtualvoidhandleHelp(){// 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();}}};classDialog:publicWidget{// ConcreteHandlerpublic:Dialog(std::shared_ptr<HelpHandler>h,Topict=NO_HELP_TOPIC):Widget(nullptr){setHandler(h.get(),t);}virtualvoidhandleHelp(){std::cout<<"Dialog::handleHelp\n";// Widget operations that Dialog overrides...if(hasHelp()){// offer help on the dialog}else{HelpHandler::handleHelp();}}};classApplication:publicHelpHandler{public:Application(Topict):HelpHandler(nullptr,t){}virtualvoidhandleHelp(){std::cout<<"Application::handleHelp\n";// show a list of help topics}};intmain(){constexprTopicPRINT_TOPIC=1;constexprTopicPAPER_ORIENTATION_TOPIC=2;constexprTopicAPPLICATION_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();}
TheCocoa andCocoa Touch frameworks, used forOS X andiOS applications respectively, actively use the chain-of-responsibility pattern for handling events. Objects that participate in the chain are calledresponder objects, inheriting from theNSResponder
(OS X)/UIResponder
(iOS) class. All view objects (NSView
/UIView
), view controller objects (NSViewController
/UIViewController
), window objects (NSWindow
/UIWindow
), and the application object (NSApplication
/UIApplication
) are responder objects.
Typically, when a view receives an event which it can't handle, it dispatches it to its superview until it reaches the view controller or window object. If the window can't handle the event, the event is dispatched to the application object, which is the last object in the chain. For example:
{{cite book}}
: CS1 maint: multiple names: authors list (link)