Chain-of-responsibility pattern: Difference between revisions

Content deleted Content added
Examples: Move Crystal example to Wikibooks
Fix compilation error in C++ code
 
(6 intermediate revisions by 6 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.
 
Line 8 ⟶ 9:
 
==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 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]]''
Line 15:
 
===What problems can the Chain of Responsibility design pattern solve?===
<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>
* 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>
 
===What solution does the Chain of Responsibility design pattern describe?===
Line 45 ⟶ 44:
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.
 
== ExamplesExample ==
<!-- 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 -->
 
=== C++ example ===
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++">
Line 67 ⟶ 65:
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;
Line 80 ⟶ 78:
Topic topic;
};
 
 
class Widget : public HelpHandler {
Line 146 ⟶ 145:
button->handleHelp();
}
</syntaxhighlight>
 
The program output is:
 
<syntaxhighlight lang="c++">
Button::handleHelp
</syntaxhighlight>
 
{{wikibooks|Computer Science Design Patterns|Chain of responsibility|Chain-of-responsibility implementations in various languages}}
 
=== PHP example ===
<syntaxhighlight lang="php">
<?php
 
abstract class Logger
{
 
/**
* Bitmask flags for severity.
*/
public const NONE = 0;
public const INFO = 0b000001;
public const DEBUG = 0b000010;
public const WARNING = 0b000100;
public const ERROR = 0b001000;
public const FUNCTIONAL_MESSAGE = 0b010000;
public const FUNCTIONAL_ERROR = 0b100000;
public const ALL = 0b111111;
 
/** @var int A bitmask flag from this class. */
protected int $logMask;
 
/** @var \Logger|null An optional next logger to handle the message */
protected ?Logger $next = null;
 
/**
* Logger constructor.
*
* @param int $mask
* A bitmask flag from this class.
*/
public function __construct(int $mask)
{
$this->logMask = $mask;
}
 
/**
* Set next responsible logger in the chain.
*
* @param \Logger $nextLogger
* Next responsible logger.
*
* @return \Logger
* Logger: Next responsible logger.
*/
public function setNext(Logger $nextLogger): Logger
{
$this->next = $nextLogger;
 
return $nextLogger;
}
 
/**
* Message writer handler.
*
* @param string $msg
* Message string.
* @param int $severity
* Severity of message as a bitmask flag from this class.
*
* @return $this
*/
public function message(string $msg, int $severity): Logger
{
if ($severity & $this->logMask) {
$this->writeMessage($msg);
}
if ($this->next !== null) {
$this->next->message($msg, $severity);
}
 
return $this;
}
 
/**
* Abstract method to write a message
*
* @param string $msg
* Message string.
*/
abstract protected function writeMessage(string $msg): void;
 
}
 
class ConsoleLogger extends Logger
{
 
protected function writeMessage(string $msg): void
{
echo "Writing to console: $msg\n";
}
 
}
 
class EmailLogger extends Logger
{
 
protected function writeMessage(string $msg): void
{
echo "Sending via email: $msg\n";
}
 
}
 
class FileLogger extends Logger
{
 
protected function writeMessage(string $msg): void
{
echo "Writing to a log file: $msg\n";
}
 
}
 
$logger = new ConsoleLogger(Logger::ALL);
$logger
->setNext(new EmailLogger(Logger::FUNCTIONAL_MESSAGE | Logger::FUNCTIONAL_ERROR))
->setNext(new FileLogger(Logger::WARNING | Logger::ERROR));
 
$logger
// Handled by ConsoleLogger since the console has a loglevel of all
->message("Entering function ProcessOrder().", Logger::DEBUG)
->message("Order record retrieved.", Logger::INFO)
// Handled by ConsoleLogger and FileLogger since filelogger implements Warning & Error
->message("Customer Address details missing in Branch DataBase.", Logger::WARNING)
->message("Customer Address details missing in Organization DataBase.", Logger::ERROR)
// Handled by ConsoleLogger and EmailLogger as it implements functional error
->message("Unable to Process Order ORD1 Dated D1 For Customer C1.", Logger::FUNCTIONAL_ERROR)
// Handled by ConsoleLogger and EmailLogger
->message("Order Dispatched.", Logger::FUNCTIONAL_MESSAGE);
 
/* Output
Writing to console: Entering function ProcessOrder().
Writing to console: Order record retrieved.
Writing to console: Customer Address details missing in Branch DataBase.
Writing to a log file: Customer Address details missing in Branch DataBase.
Writing to console: Customer Address details missing in Organization DataBase.
Writing to a log file: Customer Address details missing in Organization DataBase.
Writing to console: Unable to Process Order ORD1 Dated D1 For Customer C1.
Sending via email: Unable to Process Order ORD1 Dated D1 For Customer C1.
Writing to console: Order Dispatched.
Sending via email: Order Dispatched.
*/
</syntaxhighlight>