Chain-of-responsibility pattern


In object-oriented design, the chain-of-responsibility pattern is a design pattern 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. Thus, the chain of responsibility is an object oriented version of the if... else if... else if....... else... endif idiom, with the benefit that the condition-action blocks can be dynamically rearranged and reconfigured at runtime.
In a variation of the standard chain-of-responsibility model, some handlers may act as dispatchers, 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 might work in this manner.
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 GoF book. However, many implementations 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?
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?
This enables 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 class diagram, the Sender class doesn't refer to a particular receiver class directly.
Instead, Sender refers to the Handler interface for handling a request, 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.
The UML sequence diagram
shows the run-time interactions: In this example, the Sender object calls handleRequest on the receiver1 object.
The receiver1 forwards the request to receiver2, which in turn forwards the request to receiver3, which handles 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

C# example

This C# examples uses the logger application to select different sources based on the log level;

namespace ChainOfResponsibility
/* 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
@log_levels = of LogLevel
levels.each do |level|
@log_levels << level
end
end
def message
if @log_levels.includes? || @log_levels.includes?
write_message
end
@next.try
end
abstract def write_message
end
class ConsoleLogger < Logger
def write_message
puts "Writing to console: #"
end
end
class EmailLogger < Logger
def write_message
puts "Sending via email: #"
end
end
class FileLogger < Logger
def write_message
puts "Writing to Log File: #"
end
end
  1. Program
  2. Build the chain of responsibility
logger = ConsoleLogger.new
logger1 = logger.next = EmailLogger.new
logger2 = logger1.next = FileLogger.new
  1. Handled by ConsoleLogger since the console has a loglevel of all
logger.message
logger.message
  1. Handled by ConsoleLogger and FileLogger since filelogger implements Warning & Error
logger.message
logger.message
  1. Handled by ConsoleLogger and EmailLogger as it implements functional error
logger.message
  1. Handled by ConsoleLogger and EmailLogger
logger.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 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 example


"""
Chain of responsibility pattern example.
"""
from abc import ABCMeta, abstractmethod
from enum import Enum, auto
class LogLevel:
""" 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__ -> None:
"""Initialize new logger.
Arguments:
levels : List of log levels.
"""
self.log_levels =
for level in levels:
self.log_levels.append
def set_next:
"""Set next responsible logger in the chain.
Arguments:
next_logger : Next responsible logger.
Returns: Logger: Next responsible logger.
"""
self.next = next_logger
return self.next
def message -> None:
"""Message writer handler.
Arguments:
msg : Message string.
severity : Severity of message as log level enum.
"""
if LogLevel.ALL in self.log_levels or severity in self.log_levels:
self.write_message
if self.next is not None:
self.next.message
@abstractmethod
def write_message -> None:
"""Abstract method to write a message.
Arguments:
msg : Message string.
Raises: NotImplementedError
"""
raise NotImplementedError
class ConsoleLogger:
def write_message -> None:
"""Overrides parent's abstract method to write to console.
Arguments:
msg : Message string.
"""
print
class EmailLogger:
"""Overrides parent's abstract method to send an email.
Arguments:
msg : Message string.
"""
def write_message -> None:
print
class FileLogger:
"""Overrides parent's abstract method to write a file.
Arguments:
msg : Message string.
"""
def write_message -> None:
print
def main:
"""Building the chain of responsibility."""
logger = ConsoleLogger
email_logger = logger.set_next
# As we don't need to use file logger instance anywhere later
# We will not set any value for it.
email_logger.set_next
# ConsoleLogger will handle this part of code since the message
# has a log level of all
logger.message
logger.message
# ConsoleLogger and FileLogger will handle this part since file logger
# implements WARNING and ERROR
logger.message
logger.message
# ConsoleLogger and EmailLogger will handle this part as they implement
# functional error
logger.message
logger.message
if __name__ "__main__":
main

Implementations

Cocoa and Cocoa Touch

The 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 NSResponder /UIResponder class. All view objects, view controller objects, window objects, and the application object 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: