Chain-of-responsibility pattern: Difference between revisions

Content deleted Content added
Visual Prolog example (protected stream)
Fix compilation error in C++ code
 
(304 intermediate revisions by more than 100 users not shown)
Line 1:
{{Short description|Programming pattern}}
In [[computer programming]], the '''chain-of-responsibility pattern''' is a [[design pattern (computer science)|design pattern]] consisting of a source of [[Command pattern|command objects]] and a series of '''processing objects'''. Each processing object contains a set of logic that describes the types of command objects that it can handle, and how to pass off those that it cannot to the next processing object in the chain. A mechanism also exists for adding new processing objects to the end of this chain.
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[[Interpreter (parsed, but not yet executedcomputing)|interpreter]] might bework ain fittingthis examplemanner.
 
This pattern promotes the idea of [[loose coupling]], a common programming practice.
 
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.
== Examples ==
 
=== Java =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>
The following Java code illustrates the pattern with the example of a logging class. Each logging handler decides if any action is to be taken at this log level and then passes the message on to the next logging handler. The output is:
design pattern is one of the twenty-three well-known
Writing to debug output: Entering function y.
''[[Design Patterns|GoF design patterns]]''
Writing to debug output: Step1 completed.
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.
Writing to stderr: Step1 completed.
Writing to debug output: An error has occurred.
Sending via e-mail: An error has occurred.
Writing to stderr: An error has occurred.
Note that this example should not be seen as a recommendation to write Logging classes this way.
 
===What problems can the Chain of Responsibility design pattern solve?===
import java.util.*;
* 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.
abstract class Logger {
 
public static int ERR = 3;
Implementing a request directly within the class that sends the request is inflexible
public static int NOTICE = 5;
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>
public static int DEBUG = 7;
 
protected int mask;
===What solution does the Chain of Responsibility design pattern describe?===
 
protected Logger next; ''// the next element in the chain of responsibility''
* 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).
public Logger setNext(Logger l) { next = l; return this; }
 
This enables us to send a request to a chain of receivers
abstract public void message(String msg, int priority);
without 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.
class DebugLogger extends Logger{
 
public DebugLogger(int mask) { this.mask = mask; }
See also the UML class and sequence diagram below.
 
public void message(String msg, int priority) {
== Structure ==
if (priority <= mask) System.out.println("Writing to debug output: "+msg);
=== UML class and sequence diagram ===
if (next != null) next.message(msg, priority);
[[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.<ref>{{cite web|title=The Chain of Responsibility design pattern - Structure and Collaboration|url=http://w3sdesign.com/?gr=b01&ugr=struct|website=w3sDesign.com|access-date=2017-08-12}}</ref>]]
 
In the above [[Unified Modeling Language|UML]] [[class diagram]], the <code>Sender</code> class doesn't refer to a particular receiver class directly.
Instead, <code>Sender</code> refers to the <code>Handler</code> interface for handling a request (<code>handler.handleRequest()</code>), which makes the <code>Sender</code> independent of which receiver handles the request.
The <code>Receiver1</code>, <code>Receiver2</code>, and <code>Receiver3</code> classes implement the <code>Handler</code> interface by either handling or forwarding a request (depending on run-time conditions).
<br>
The [[Unified Modeling Language|UML]] [[sequence diagram]]
shows the run-time interactions: In this example, the <code>Sender</code> object calls <code>handleRequest()</code> on the <code>receiver1</code> object (of type <code>Handler</code>).
The <code>receiver1</code> forwards the request to <code>receiver2</code>, which in turn forwards the request to <code>receiver3</code>, which handles (performs) the request.
 
== Example ==
<!-- 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
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 EMailLogger extends Logger{
class Dialog : public Widget { // ConcreteHandler
public EMailLogger(int mask) { this.mask = mask; }
public:
Dialog(std::shared_ptr<HelpHandler> h, Topic t = NO_HELP_TOPIC) : Widget(nullptr) {
public void message(String msg, int priority) {
setHandler(h.get(), t);
if (priority <= mask) System.out.println("Sending via e-mail: "+msg);
}
if (next != null) next.message(msg, priority);
virtual void handleHelp() {
std::cout << "Dialog::handleHelp\n";
// Widget operations that Dialog overrides...
if(hasHelp()) {
// offer help on the dialog
} else {
HelpHandler::handleHelp();
}
}
};
class StderrLogger extends Logger{
public StderrLogger(int mask) { this.mask = mask; }
public void message(String msg, int priority) {
if (priority <= mask) System.out.println("Writing to stderr: "+msg);
if (next != null) next.message(msg, priority);
}
}
class ChainOfResponsibilityExample{
public static void main(String[] args) {
// building the chain of responsibility
Logger l = new DebugLogger(Logger.DEBUG).setNext(
new EMailLogger(Logger.ERR).setNext(
new StderrLogger(Logger.NOTICE) ) );
l.message("Entering function y.", Logger.DEBUG); ''// handled by DebugLogger''
l.message("Step1 completed.", Logger.NOTICE); ''// handled by Debug- and StderrLogger''
l.message("An error has occurred.", Logger.ERR); ''// handled by all three Logger''
}
}
 
class Application : public HelpHandler {
=== [[Visual Prolog]] ===
public:
Application(Topic t) : HelpHandler(nullptr, t) {}
virtual void handleHelp() {
std::cout << "Application::handleHelp\n";
// show a list of help topics
}
};
 
int main() {
This is an almost realistic example of protecting the operations on a stream with a named mutex.
constexpr Topic PRINT_TOPIC = 1;
constexpr Topic PAPER_ORIENTATION_TOPIC = 2;
First the outputStream interface (a simplified version of the real one):
constexpr Topic APPLICATION_TOPIC = 3;
// The smart pointers prevent memory leaks.
<font color="#808000">interface</font> outputStream
std::shared_ptr<Application> application = std::make_shared<Application>(APPLICATION_TOPIC);
<font color="#808000">predicates</font>
std::shared_ptr<Dialog> dialog = std::make_shared<Dialog>(application, PRINT_TOPIC);
write : <font color="#993300">(</font>...<font color="#993300">)</font>.
std::shared_ptr<Button> button = std::make_shared<Button>(dialog, PAPER_ORIENTATION_TOPIC);
writef : <font color="#993300">(</font>string <font color="#008000">FormatString</font>, ...<font color="#993300">)</font>.
<font color="#808000">end</font> <font color="#808000">interface</font> outputStream
This class encapsulates each of the stream operations the mutex. The '''finally''' predicate is used to ensure that the mutex is released no matter how the operation goes (i.e. also in case of excetions). The underlying mutex object is released by a ''finalizer'' in the mutex class, and for this example we leave it at that.
<font color="#808000">class</font> outputStream_protected : outputStream
<font color="#808000">constructors</font>
new : <font color="#993300">(</font>string <font color="#008000">MutexName</font>, outputStream <font color="#008000">Stream</font><font color="#993300">)</font>.
<font color="#808000">end</font> <font color="#808000">class</font> outputStream_protected
<font color="#800080">#include</font> <font color="#0000FF">@&quot;pfc\multiThread\multiThread.ph&quot;</font>
<font color="#808000">implement</font> outputStream_protected
<font color="#808000">facts</font>
mutex : mutex.
stream : outputStream.
<font color="#808000">clauses</font>
new<font color="#993300">(</font><font color="#008000">MutexName</font>, <font color="#008000">Stream</font><font color="#993300">)</font> :-
mutex := mutex::createNamed<font color="#993300">(</font><font color="#008000">MutexName</font>, false<font color="#993300">)</font>, <font color="#AA77BD">% do not take ownership</font>
stream := <font color="#008000">Stream</font>.
<font color="#808000">clauses</font>
write<font color="#993300">(</font>...<font color="#993300">)</font> :-
_ = mutex:wait<font color="#993300">()</font>, <font color="#AA77BD">% ignore wait code in this simplified example</font>
<font color="#333399">finally</font><font color="#993300">(</font>stream:write<font color="#993300">(</font>...<font color="#993300">)</font>, mutex:release<font color="#993300">())</font>.
<font color="#808000">clauses</font>
writef<font color="#993300">(</font><font color="#008000">FormatString</font>, ...<font color="#993300">)</font> :-
_ = mutex:wait<font color="#993300">()</font>, <font color="#AA77BD">% ignore wait code in this simplified example</font>
<font color="#333399">finally</font><font color="#993300">(</font>stream:writef<font color="#993300">(</font><font color="#008000">FormatString</font>, ...<font color="#993300">)</font>, mutex:release<font color="#993300">())</font>.
<font color="#808000">end</font> <font color="#808000">implement</font> outputStream_protected
 
button->handleHelp();
Usage example, the '''client''' uses an '''outputStream''' instead of receiving the pipeStream directly, it get the protected version of it.
}
</syntaxhighlight>
 
== Implementations ==
<font color="#800080">#include</font> <font color="#0000FF">@&quot;pfc\pipe\pipe.ph&quot;</font>
=== Cocoa and Cocoa Touch ===
<font color="#808000">goal</font>
<font color="#008000">Stream</font> = pipeStream::openClient<font color="#993300">(</font><font color="#0000FF">&quot;TestPipe&quot;</font><font color="#993300">)</font>,
<font color="#008000">Protected</font> = outputStream_protected::new<font color="#993300">(</font><font color="#0000FF">&quot;PipeMutex&quot;</font>, <font color="#008000">Stream</font><font color="#993300">)</font>,
client<font color="#993300">(</font><font color="#008000">Protected</font><font color="#993300">)</font>,
<font color="#008000">Stream</font>:close<font color="#993300">()</font>.
 
The [[Cocoa (API)|Cocoa]] and [[Cocoa Touch]] frameworks, used for [[OS X]] and [[iOS]] applications respectively, actively use the chain-of-responsibility pattern for handling events. Objects that participate in the chain are called ''responder'' objects, inheriting from the <code>NSResponder</code> (OS X)/<code>UIResponder</code> (iOS) class. All view objects (<code>NSView</code>/<code>UIView</code>), view controller objects (<code>NSViewController</code>/<code>UIViewController</code>), window objects (<code>NSWindow</code>/<code>UIWindow</code>), and the application object (<code>NSApplication</code>/<code>UIApplication</code>) are responder objects.
==See also==
*[[Interception pattern]]
*[[Interceptor pattern]]
 
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:
==External links==
 
*Article "[http://www.javaworld.com/javaworld/jw-08-2004/jw-0816-chain.html The Chain of Responsibility pattern's pitfalls and improvements]" by [[Michael Xinsheng Huang]]
* On OS X, moving a textured window with the mouse can be done from any ___location (not just the title bar), unless on that ___location there's a view which handles dragging events, like slider controls. If no such view (or superview) is there, dragging events are sent up the chain to the window which does handle the dragging event.
*Article "[http://www.javaworld.com/javaworld/jw-08-2003/jw-0829-designpatterns.html Follow the Chain of Responsibility]" by [[David Geary]]
* On iOS, it's typical to handle view events in the view controller which manages the view hierarchy, instead of subclassing the view itself. Since a view controller lies in the responder chain after all of its managed subviews, it can intercept any view events and handle them.
*Article "[http://developer.com/java/other/article.php/631261 Pattern Summaries: Chain of Responsibility]" by [[Mark Grand]]
 
*[http://dofactory.com/Patterns/PatternChain.aspx CoR overview]
== See also ==
*[http://allapplabs.com/java_design_patterns/chain_of_responsibility_pattern.htm Behavioral Patterns - Chain of Responsibility Pattern]
* [[Software design pattern]]
*[http://home.earthlink.net/~huston2/dp/chain.html Chain of Responsibility]
* [[Single responsibility principle]]
*[http://c2.com/cgi/wiki?ChainOfResponsibilityPattern Descriptions from Portland Pattern Repository]
 
== References ==
{{reflist}}
 
{{Design Patterns Patterns}}
 
{{DEFAULTSORT:Design Pattern (Computer Science)}}
[[Category:Software design patterns]]
[[Category:Articles with example Java code]]
 
[[de:Zuständigkeitskette]]
[[es:Chain of Responsibility (patrón de diseño)]]
[[fr:Cha%C3%AEne_de_responsabilit%C3%A9_%28motif_de_conception%29]]