Chain of Responsibility Pattern
(from Design
Patterns)
Synopsis
Avoid coupling the sender of a request to its receiver by giving more than
one object a chance to handle the request. Chain the receiving objects and
pass the request along the chain until an object handles it.
Context
The particular object which can fulfill a request may vary with time or
with the specifics of the request. For example, the window which handles a
mouse event, the class which handles a method call, the scope which
resolves a variable reference, the clerk which isn't busy, or the format
used for reading an image.
Forces
-
You want to develop the sender and all of the handlers separately, to
minimize coupling (what they know about eachother) and increase reuse
(using one without the other).
-
The choice of appropriate handler is highly dynamic.
Solution
Give each handler the ability to reject a request. Then order the
potential handlers in a chain-like data structure. The first object in the
chain receives the request and either handles it or rejects it. If it
rejects, give the request to the next object, and repeat until an object
handles it or you run out of objects.
Consequences
-
Low coupling because the sender and all handlers are oblivious to each
other.
-
Since each handler decides at that moment whether it can handle the
request, the handler choice is very flexible.
-
The chain can be reordered, added to, or removed from at run-time,
creating added flexibility in how requests are handled.
-
There is no guarantee that a request will be handled.
Implementation
-
If the distribution of handled requests is non-uniform between the
handlers, the chain can self-optimize by moving a handler to the front
every time it fields a request.
-
The chain can also be accelerated by caching the choice of handler for each
type of request. This is best when there aren't many different requests
and the choice of handler does not vary with time too rapidly. If the
configuration of the chain changes, the cache can always be flushed and
rebuilt dynamically. Alternatively, the cache can be interpreted as merely
a "hint" and not trusted completely. Caching is an alternative to
reordering when the order of the handlers has additional meaning, as in
class hierarchies and window hierarchies.
-
Instead of having a reject option, handlers could be given a pointer to
their successor, which they could forward to directly instead of rejecting.
Unfortunately, this prevents transparent reorganization tricks like the above.
-
To prevent unhandled requests, the last member of the chain can be
designed to search for more handlers or perform the error condition
directly.
-
Chain of Responsibility can be used with a Composite hierarchy, where a
component's parent can act as its successor. It can also be an observer in
the Observer pattern.
-
The members of a Chain of Responsibility can be individual functions
instead of objects. In either case, the members of the chain must have the
same type.
See Design
Patterns for sample code.
Known Uses
-
The X Window System, MacApp, Windows, and ET++ use the Chain of
Responsibility pattern to handle input, redraw, and modification events.
It can also be used for context-sensitive help events. A window which
isn't interested in the event passes it up to its enclosing window. This
is an example of combining Chain of Responsibility with Composite.
-
The Python and Self languages use the Chain of Responsibility pattern to
dispatch method calls and variable references to the appropriate class.
Changes to classes are immediately visible in their descendants and
instances. Self also uses the caching optimization, where the cache is
invalidated whenever a class object changes.
-
The Tcl language uses a Chain of Responsibility to pair variable references
with the appropriate scope. At the end of the chain is the procedure named
unknown, which can complete the search in a user-defined way,
such as automatically loading the relevant source file.
-
Tk uses a Chain of Responsibility to determine the format of an image file
and read it in.
-
A Chain of Responsibility can be used to implement the Backup pattern (from
PLoP'95),
where slower but more reliable algorithms are tried if the preferred one
fails.
- More uses are described in this student project.
The Chain of Responsibility is not a good choice for finding the
best receiver of a message. For example, C++ resolves overloaded
methods by minimizing the number of argument conversions required. The
different methods cannot be ordered in a single chain to simulate this
behavior.
Thomas Minka
Last modified: Fri Sep 02 16:57:16 GMT 2005