Content deleted Content added
mNo edit summary |
Fix compilation error in C++ code |
||
(45 intermediate revisions by 25 users not shown) | |||
Line 1:
{{Short description|Programming pattern}}
In [[object-oriented design]], the '''chain-of-responsibility pattern''' is a [[Behavioral pattern|behavioral]] [[design pattern (computer science)|design pattern]] consisting of a source of [[Command pattern|command objects]] and a series of '''processing objects'''.<ref>{{Cite web |url=http://www.blackwasp.co.uk/ChainOfResponsibility.aspx |title=Chain of Responsibility Design Pattern |access-date=2013-11-08 |archive-url=https://web.archive.org/web/20180227070352/http://www.blackwasp.co.uk/ChainOfResponsibility.aspx |archive-date=2018-02-27 |url-status=dead }}</ref> 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 as [[dynamic dispatch|dispatcher]]s, capable of sending commands out in a variety of directions, forming a ''tree 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. An [[XML]] [[Interpreter (computing)|interpreter]] might work in this manner.
Line 5 ⟶ 6:
This pattern promotes the idea of [[loose coupling]].
The chain-of-responsibility pattern is structurally nearly identical to the [[decorator 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 the [[Design Patterns|GoF]] 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.
==Overview==
The Chain of Responsibility<ref name="GoF">{{cite book|author=Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides|title=Design Patterns: Elements of Reusable Object-Oriented Software|year=1994|publisher=Addison Wesley|isbn=0-201-63361-2|pages=[https://archive.org/details/designpatternsel00gamm/page/223 223ff]|url-access=registration|url=https://archive.org/details/designpatternsel00gamm/page/223}}</ref>
design pattern is one of the twenty-three well-known
''[[Design Patterns|GoF 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.
* Coupling the sender of a request to its receiver should be avoided.
* It should be possible that more than one receiver can handle a request.
Implementing a request directly within the class that sends the request is inflexible
because it couples the class to a particular receiver and makes it impossible to support multiple receivers.<ref>{{cite web|title=The Chain of Responsibility design pattern - Problem, Solution, and Applicability|url=http://w3sdesign.com/?gr=b01&ugr=proble|website=w3sDesign.com|access-date=2017-08-12}}</ref>
* Define a chain of receiver objects having the responsibility, depending on run-time conditions, to either handle a request or forward it to the next receiver on the chain (if any).
This enables us to send a request to a chain of receivers
without having to know which one handles the request.
The request gets passed along the chain until a receiver handles the request.
Line 35 ⟶ 34:
== Structure ==
=== UML class and sequence diagram ===
[[File:w3sDesign Chain of Responsibility Design Pattern UML.jpg|frame|none|A sample UML class and sequence diagram for the Chain of Responsibility design pattern.
In the above [[Unified Modeling Language|UML]] [[class diagram]], the <code>Sender</code> class doesn't refer to a particular receiver class directly.
Line 48 ⟶ 47:
<!-- Wikipedia is not a list of examples. Do not add examples from your favorite programming language here; this page exists to explain the design pattern, not to show how it interacts with subtleties of every language under the sun. Feel free to add examples here: http://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Chain_of_responsibility -->
This C++11 implementation is based on the pre C++98 implementation in the book.<ref>{{cite book |author=Erich Gamma |title=Design Patterns: Elements of Reusable Object-Oriented Software |publisher=Addison Wesley |year=1994 |isbn=0-201-63361-2 |pages=189 ff.}}</ref>
<syntaxhighlight lang="c++">
#include <iostream>
#include <memory>
typedef int Topic;
constexpr Topic NO_HELP_TOPIC = -1;
// defines an interface for handling requests.
class HelpHandler { // Handler
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) {
}
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();
}
</syntaxhighlight>
== Implementations ==
|