previous up next Chain of Responsibility

Intent, Motivation, Applicability, Structure, Participants, Collaborations, Consequences, Implementation, Sample Code, Known Uses, Related Patterns

previous next Sample Code

The following example illustrates how a chain of responsibility can handle requests for an on-line help system like the one described earlier. The help request is an explicit operation. We'll use existing parent references in the widget hierarchy to propagate requests between widgets in the chain, and we'll define a reference in the Handler class to propagate help requests between nonwidgets in the chain.

The HelpHandler class defines the interface for handling help requests. It maintains a help topic (which is empty by default) and keeps a reference to its successor on the chain of help handlers. The key operation is HandleHelp, which subclasses override. HasHelp is a convenience operation for checking whether there is an associated help topic.

typedef int Topic; const Topic NO_HELP_TOPIC = -1; class HelpHandler { public: HelpHandler(HelpHandler* = 0, Topic = NO_HELP_TOPIC); virtual bool HasHelp(); virtual void SetHandler(HelpHandler*, Topic); virtual void HandleHelp(); private: HelpHandler* _successor; Topic _topic; }; HelpHandler::HelpHandler ( HelpHandler* h, Topic t ) : _successor(h), _topic(t) { } bool HelpHandler::HasHelp () { return _topic != NO_HELP_TOPIC; } void HelpHandler::HandleHelp () { if (_successor != 0) { _successor->HandleHelp(); } }
All widgets are subclasses of the Widget abstract class. Widget is a subclass of HelpHandler, since all user interface elements can have help associated with them. (We could have used a mixin-based implementation just as well.)
class Widget : public HelpHandler { protected: Widget(Widget* parent, Topic t = NO_HELP_TOPIC); private: Widget* _parent; }; Widget::Widget (Widget* w, Topic t) : HelpHandler(w, t) { _parent = w; }
In our example, a button is the first handler on the chain. The Button class is a subclass of Widget. The Button constructor takes two parameters: a reference to its enclosing widget and the help topic.
class Button : public Widget { public: Button(Widget* d, Topic t = NO_HELP_TOPIC); virtual void HandleHelp(); // Widget operations that Button overrides... };

Button's version of HandleHelp first tests to see if there is a help topic for buttons. If the developer hasn't defined one, then the request gets forwarded to the successor using the HandleHelp operation in HelpHandler. If there is a help topic, then the button displays it, and the search ends.

Button::Button (Widget* h, Topic t) : Widget(h, t) { } void Button::HandleHelp () { if (HasHelp()) { // offer help on the button } else { HelpHandler::HandleHelp(); } }
Dialog implements a similar scheme, except that its successor is not a widget but any help handler. In our application this successor will be an instance of Application.
class Dialog : public Widget { public: Dialog(HelpHandler* h, Topic t = NO_HELP_TOPIC); virtual void HandleHelp(); // Widget operations that Dialog overrides... // ... }; Dialog::Dialog (HelpHandler* h, Topic t) : Widget(0) { SetHandler(h, t); } void Dialog::HandleHelp () { if (HasHelp()) { // offer help on the dialog } else { HelpHandler::HandleHelp(); } }
At the end of the chain is an instance of Application. The application is not a widget, so Application is subclassed directly from HelpHandler. When a help request propagates to this level, the application can supply information on the application in general, or it can offer a list of different help topics:
class Application : public HelpHandler { public: Application(Topic t) : HelpHandler(0, t) { } virtual void HandleHelp(); // application-specific operations... }; void Application::HandleHelp () { // show a list of help topics }
The following code creates and connects these objects. Here the dialog concerns printing, and so the objects have printing-related topics assigned.
const Topic PRINT_TOPIC = 1; const Topic PAPER_ORIENTATION_TOPIC = 2; const Topic APPLICATION_TOPIC = 3; Application* application = new Application(APPLICATION_TOPIC); Dialog* dialog = new Dialog(application, PRINT_TOPIC); Button* button = new Button(dialog, PAPER_ORIENTATION_TOPIC);
We can invoke the help request by calling HandleHelp on any object on the chain. To start the search at the button object, just call HandleHelp on it:
button->HandleHelp();
In this case, the button will handle the request immediately. Note that any HelpHandler class could be made the successor of Dialog. Moreover, its successor could be changed dynamically. So no matter where a dialog is used, you'll get the proper context-dependent help information for it.


Sample Code of Abstract Factory, Adapter, Bridge, Builder, Chain of Responsibility, Command, Composite, Decorator, Facade, Factory Method, Flyweight, Interpreter, Iterator, Mediator, Memento, Observer, Prototype, Proxy, Singleton, State, Strategy, Template Method, Visitor

previous next