HOME

TheInfoList



OR:

In
object-oriented design Object-oriented design (OOD) is the process of planning a Object-oriented programming, system of interacting objects for the purpose of solving a software problem. It is one approach to software design. Overview An Object (computer science), obje ...
, the chain-of-responsibility pattern is a
behavioral Behavior (American English) or behaviour (British English) is the range of actions and mannerisms made by individuals, organisms, systems or artificial entities in some environment. These systems can include other systems or organisms as we ...
design pattern A design pattern is the re-usable form of a solution to a design problem. The idea was introduced by the architect Christopher Alexander and has been adapted for various other disciplines, particularly software engineering. The "Gang of Four" boo ...
consisting of a source of command objects and a series of processing objects. 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
dispatcher A dispatcher is a communications worker who receives and transmits information to coordinate operations of other personnel and vehicles carrying out a service. A number of organizations, including police and fire departments, emergency medical ...
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 Extensible Markup Language (XML) is a markup language and file format for storing, transmitting, and reconstructing arbitrary data. It defines a set of rules for encoding documents in a format that is both human-readable and machine-readable ...
interpreter might work in this manner. This pattern promotes the idea of
loose coupling In computing and systems design, a loosely coupled system is one # in which components are weakly associated (have breakable relationships) with each other, and thus changes in one component least affect existence or performance of another comp ...
. The chain-of-responsibility pattern is structurally nearly identical to the
decorator pattern In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class. The decorator pattern is often ...
, 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 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 design pattern is one of the twenty-three well-known '' 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.


What problems can the Chain of Responsibility design pattern solve?

* 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.


What solution does the Chain of Responsibility design pattern describe?

* 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. The sender of a request is no longer coupled to a particular receiver. See also the UML class and sequence diagram below.


Structure


UML class and sequence diagram

In the above
UML The Unified Modeling Language (UML) is a general-purpose, developmental modeling language in the field of software engineering that is intended to provide a standard way to visualize the design of a system. The creation of UML was originally m ...
class diagram In software engineering, a class diagram in the Unified Modeling Language (UML) is a type of static structure diagram that describes the structure of a system by showing the system's classes, their attributes, operations (or methods), and the rela ...
, the Sender class doesn't refer to a particular receiver class directly. Instead, Sender refers to the Handler interface for handling a request (handler.handleRequest()), which makes the Sender independent of which receiver handles the request. The Receiver1, Receiver2, and Receiver3 classes implement the Handler interface by either handling or forwarding a request (depending on run-time conditions).
The
UML The Unified Modeling Language (UML) is a general-purpose, developmental modeling language in the field of software engineering that is intended to provide a standard way to visualize the design of a system. The creation of UML was originally m ...
sequence diagram A sequence diagram or system sequence diagram (SSD) shows process interactions arranged in time sequence in the field of software engineering. It depicts the processes involved and the sequence of messages exchanged between the processes needed ...
shows the run-time interactions: In this example, the Sender object calls handleRequest() on the receiver1 object (of type Handler). The receiver1 forwards the request to receiver2, which in turn forwards the request to receiver3, which handles (performs) the request.


Example


Java example

Below is an example of this pattern in Java. A logger is created using a chain of loggers, each one configured with different log levels. import java.util.Arrays; import java.util.EnumSet; import java.util.function.Consumer; @FunctionalInterface public interface Logger class Runner


C# example

This C# examples uses the logger application to select different sources based on the log level; namespace ChainOfResponsibility; lagspublic enum LogLevel /// /// Abstract Handler in chain of responsibility pattern. /// public abstract class Logger public class ConsoleLogger : Logger public class EmailLogger : Logger class FileLogger : Logger public class Program /* 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 Log File: Customer Address details missing in Branch DataBase. Writing to console: Customer Address details missing in Organization DataBase. Writing to 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. */


Crystal example

enum LogLevel None Info Debug Warning Error FunctionalMessage FunctionalError All end abstract class Logger property log_levels property next : Logger , Nil def initialize(*levels) @log_levels = [] of LogLevel levels.each do , level, @log_levels << level end end def message(msg : String, severity : LogLevel) if @log_levels.includes?(LogLevel::All) , , @log_levels.includes?(severity) write_message(msg) end @next.try(&.message(msg, severity)) end abstract def write_message(msg : String) end class ConsoleLogger < Logger def write_message(msg : String) puts "Writing to console: #" end end class EmailLogger < Logger def write_message(msg : String) puts "Sending via email: #" end end class FileLogger < Logger def write_message(msg : String) puts "Writing to Log File: #" end end # Program # Build the chain of responsibility logger = ConsoleLogger.new(LogLevel::All) logger1 = logger.next = EmailLogger.new(LogLevel::FunctionalMessage, LogLevel::FunctionalError) logger2 = logger1.next = FileLogger.new(LogLevel::Warning, LogLevel::Error) # Handled by ConsoleLogger since the console has a loglevel of all logger.message("Entering function ProcessOrder().", LogLevel::Debug) logger.message("Order record retrieved.", LogLevel::Info) # Handled by ConsoleLogger and FileLogger since filelogger implements Warning & Error logger.message("Customer Address details missing in Branch DataBase.", LogLevel::Warning) logger.message("Customer Address details missing in Organization DataBase.", LogLevel::Error) # Handled by ConsoleLogger and EmailLogger as it implements functional error logger.message("Unable to Process Order ORD1 Dated D1 For Customer C1.", LogLevel::FunctionalError) # Handled by ConsoleLogger and EmailLogger logger.message("Order Dispatched.", LogLevel::FunctionalMessage) 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 Log File: Customer Address details missing in Branch DataBase.
Writing to console: Customer Address details missing in Organization DataBase.
Writing to 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.


Python 3 example

""" Chain of responsibility pattern example. """ from abc import ABCMeta, abstractmethod from enum import Enum, auto class LogLevel(Enum): """ Log Levels Enum.""" NONE = auto() INFO = auto() DEBUG = auto() WARNING = auto() ERROR = auto() FUNCTIONAL_MESSAGE = auto() FUNCTIONAL_ERROR = auto() ALL = auto() class Logger: """Abstract handler in chain of responsibility pattern.""" __metaclass__ = ABCMeta next = None def __init__(self, levels) -> None: """Initialize new logger. Arguments: levels (list tr: List of log levels. """ self.log_levels = [] for level in levels: self.log_levels.append(level) def set_next(self, next_logger: Logger): """Set next responsible logger in the chain. Arguments: next_logger (Logger): Next responsible logger. Returns: Logger: Next responsible logger. """ self.next = next_logger return self.next def message(self, msg: str, severity: LogLevel) -> None: """Message writer handler. Arguments: msg (str): Message string. severity (LogLevel): Severity of message as log level enum. """ if LogLevel.ALL in self.log_levels or severity in self.log_levels: self.write_message(msg) if self.next is not None: self.next.message(msg, severity) @abstractmethod def write_message(self, msg: str) -> None: """Abstract method to write a message. Arguments: msg (str): Message string. Raises: NotImplementedError """ raise NotImplementedError("You should implement this method.") class ConsoleLogger(Logger): def write_message(self, msg: str) -> None: """Overrides parent's abstract method to write to console. Arguments: msg (str): Message string. """ print("Writing to console:", msg) class EmailLogger(Logger): """Overrides parent's abstract method to send an email. Arguments: msg (str): Message string. """ def write_message(self, msg: str) -> None: print(f"Sending via email: ") class FileLogger(Logger): """Overrides parent's abstract method to write a file. Arguments: msg (str): Message string. """ def write_message(self, msg: str) -> None: print(f"Writing to log file: ") def main(): """Building the chain of responsibility.""" logger = ConsoleLogger( ogLevel.ALL email_logger = logger.set_next( EmailLogger( ogLevel.FUNCTIONAL_MESSAGE, LogLevel.FUNCTIONAL_ERROR ) # As we don't need to use file logger instance anywhere later # We will not set any value for it. email_logger.set_next( FileLogger( ogLevel.WARNING, LogLevel.ERROR ) # ConsoleLogger will handle this part of code since the message # has a log level of all logger.message("Entering function ProcessOrder().", LogLevel.DEBUG) logger.message("Order record retrieved.", LogLevel.INFO) # ConsoleLogger and FileLogger will handle this part since file logger # implements WARNING and ERROR logger.message( "Customer Address details missing in Branch DataBase.", LogLevel.WARNING ) logger.message( "Customer Address details missing in Organization DataBase.", LogLevel.ERROR ) # ConsoleLogger and EmailLogger will handle this part as they implement # functional error logger.message( "Unable to Process Order ORD1 Dated D1 for customer C1.", LogLevel.FUNCTIONAL_ERROR ) logger.message("OrderDispatched.", LogLevel.FUNCTIONAL_MESSAGE) if __name__

"__main__": main()


PHP example

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. */


Implementations


Cocoa and Cocoa Touch

The Cocoa and
Cocoa Touch Cocoa Touch is the application development environment for building software programs to run on iOS for the iPhone and iPod Touch, iPadOS for the iPad, watchOS for the Apple Watch, and tvOS for the Apple TV, from Apple Inc. Cocoa Touch ...
frameworks, used for OS X and
iOS iOS (formerly iPhone OS) is a mobile operating system created and developed by Apple Inc. exclusively for its hardware. It is the operating system that powers many of the company's mobile devices, including the iPhone; the term also include ...
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 NSResponder (OS X)/UIResponder (iOS) class. All view objects (NSView/UIView), view controller objects (NSViewController/UIViewController), window objects (NSWindow/UIWindow), and the application object (NSApplication/UIApplication) are responder objects. 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: * 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. * 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.


See also

*
Software design pattern In software engineering, a software design pattern is a general, reusable solution to a commonly occurring problem within a given context in software design. It is not a finished design that can be transformed directly into source or machine co ...
*
Single responsibility principle The single-responsibility principle (SRP) is a computer programming principle that states that "A module should be responsible to one, and only one, actor." The term actor refers to a group (consisting of one or more stakeholders or users) that ...


References

{{DEFAULTSORT:Design Pattern (Computer Science) Software design patterns Articles with example Java code