Skip to content

Chain of Responsibility Pattern

Category: Behavioral Pattern

Overview

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, allowing multiple objects to handle a request without the sender knowing which object will ultimately process it.

Usage Guidelines

Use when:

  • More than one object can handle a request
  • Set of handlers should be determined at runtime
  • Sender shouldn't know which handler processes the request
  • Implementing middleware or filter chains

Avoid when:

  • Only one object can handle requests
  • Every request must be handled with guarantee
  • Chain traversal overhead is unacceptable for performance
  • Simple routing with direct handler selection is straightforward

Implementation

from typing import Optional

class RequestHandler:
    """Base class for handling requests in a chain of responsibility."""

    def __init__(self, successor: Optional['RequestHandler'] = None) -> None:
        """Initializes the handler with an optional successor.

        Args:
            successor: The next handler in the chain.
        """
        self.successor = successor

    def handle(self, request: str) -> str:
        """Handles the request or passes it to the next handler.

        Args:
            request: The request to be handled.

        Returns:
            str: The result of the request.
        """
        if self.successor:
            return self.successor.handle(request)
        return f"No handler for {request}"

class ApprovalRequestHandler(RequestHandler):
    """Handler that processes approval requests."""

    def handle(self, request: str) -> str:
        """Handles specific requests for ApprovalRequestHandler.

        Args:
            request: The request to be handled.

        Returns:
            str: The result of handling the request.
        """
        if request == "ApprovalRequest":
            return "ApprovalRequestHandler handled ApprovalRequest"
        return super().handle(request)

class EscalationRequestHandler(RequestHandler):
    """Handler that processes escalation requests."""

    def handle(self, request: str) -> str:
        """Handles specific requests for EscalationRequestHandler.

        Args:
            request: The request to be handled.

        Returns:
            str: The result of handling the request.
        """
        if request == "EscalationRequest":
            return "EscalationRequestHandler handled EscalationRequest"
        return super().handle(request)

Usage

# Creating the chain of responsibility
escalation_handler = EscalationRequestHandler()
approval_handler = ApprovalRequestHandler(escalation_handler)

# Testing the chain with different requests
print(approval_handler.handle("ApprovalRequest"))
# Output: ApprovalRequestHandler handled ApprovalRequest

print(approval_handler.handle("EscalationRequest"))
# Output: EscalationRequestHandler handled EscalationRequest

print(approval_handler.handle("FeedbackRequest"))
# Output: No handler for FeedbackRequest

Trade-offs

Benefits:

  1. Reduced coupling as sender doesn't need to know the receiver
  2. Flexibility to add or remove handlers at runtime
  3. Multiple objects share handling responsibility
  4. Single Responsibility Principle with each handler focused on one type

Drawbacks:

  1. Request might not be handled by any handler
  2. Chain traversal can be slow for performance
  3. Hard to track which handler processes request for debugging
  4. Long chains become difficult to manage

Real-World Examples

  • GUI event propagation through widget hierarchy
  • Logging frameworks with messages passing through handlers
  • HTTP middleware request processing
  • Support ticket escalation systems
  • Composite
  • Command
  • Decorator

API Reference

design_patterns.behavioral.chain_of_responsibility

Chain of Responsibility Pattern Example

This module demonstrates the Chain of Responsibility design pattern in Python. It defines a series of handler classes that process requests. Each handler can either handle a request or pass it to the next handler in the chain.

Example

Creating a chain of handlers and testing it:

# Creating the chain of responsibility
handler_chain = ApprovalRequestHandler(EscalationRequestHandler())

# Testing the chain with different requests
print(handler_chain.handle("ApprovalRequest"))  # Output: ApprovalRequestHandler handled ApprovalRequest
print(handler_chain.handle("EscalationRequest"))  # Output: EscalationRequestHandler handled EscalationRequest
print(handler_chain.handle("FeedbackRequest"))  # Output: No handler for FeedbackRequest

Classes:

Name Description
RequestHandler

Base class for handling requests.

ApprovalRequestHandler

Handles approval requests.

EscalationRequestHandler

Handles escalation requests.

ApprovalRequestHandler

Bases: RequestHandler

Handler that processes approval requests.

This handler is responsible for handling requests related to approvals.

Source code in src/design_patterns/behavioral/chain_of_responsibility.py
class ApprovalRequestHandler(RequestHandler):
    """Handler that processes approval requests.

    This handler is responsible for handling requests related to approvals.
    """

    def handle(self, request: str) -> str:
        """Handles specific requests for ApprovalRequestHandler.

        Args:
            request (str): The request to be handled.

        Returns:
            str: The result of handling the request.
        """
        if request == "ApprovalRequest":
            return "ApprovalRequestHandler handled ApprovalRequest"
        return super().handle(request)

handle(request)

Handles specific requests for ApprovalRequestHandler.

Parameters:

Name Type Description Default
request str

The request to be handled.

required

Returns:

Name Type Description
str str

The result of handling the request.

Source code in src/design_patterns/behavioral/chain_of_responsibility.py
def handle(self, request: str) -> str:
    """Handles specific requests for ApprovalRequestHandler.

    Args:
        request (str): The request to be handled.

    Returns:
        str: The result of handling the request.
    """
    if request == "ApprovalRequest":
        return "ApprovalRequestHandler handled ApprovalRequest"
    return super().handle(request)

EscalationRequestHandler

Bases: RequestHandler

Handler that processes escalation requests.

This handler is responsible for handling requests related to escalations.

Source code in src/design_patterns/behavioral/chain_of_responsibility.py
class EscalationRequestHandler(RequestHandler):
    """Handler that processes escalation requests.

    This handler is responsible for handling requests related to escalations.
    """

    def handle(self, request: str) -> str:
        """Handles specific requests for EscalationRequestHandler.

        Args:
            request (str): The request to be handled.

        Returns:
            str: The result of handling the request.
        """
        if request == "EscalationRequest":
            return "EscalationRequestHandler handled EscalationRequest"
        return super().handle(request)

handle(request)

Handles specific requests for EscalationRequestHandler.

Parameters:

Name Type Description Default
request str

The request to be handled.

required

Returns:

Name Type Description
str str

The result of handling the request.

Source code in src/design_patterns/behavioral/chain_of_responsibility.py
def handle(self, request: str) -> str:
    """Handles specific requests for EscalationRequestHandler.

    Args:
        request (str): The request to be handled.

    Returns:
        str: The result of handling the request.
    """
    if request == "EscalationRequest":
        return "EscalationRequestHandler handled EscalationRequest"
    return super().handle(request)

RequestHandler

Base class for handling requests in a chain of responsibility.

This class defines the interface for handling requests and allows for the creation of a chain of handlers. Each handler can process specific requests or pass them to the next handler in the chain.

Source code in src/design_patterns/behavioral/chain_of_responsibility.py
class RequestHandler:
    """Base class for handling requests in a chain of responsibility.

    This class defines the interface for handling requests and allows
    for the creation of a chain of handlers. Each handler can process
    specific requests or pass them to the next handler in the chain.
    """

    def __init__(self, successor: Optional['RequestHandler'] = None) -> None:
        """Initializes the handler with an optional successor.

        Args:
            successor (Optional[RequestHandler]): The next handler in the chain.
        """
        self.successor = successor

    def handle(self, request: str) -> str:
        """Handles the request or passes it to the next handler.

        Args:
            request (str): The request to be handled.

        Returns:
            str: The result of the request.
        """
        if self.successor:
            return self.successor.handle(request)
        return f"No handler for {request}"

__init__(successor=None)

Initializes the handler with an optional successor.

Parameters:

Name Type Description Default
successor Optional[RequestHandler]

The next handler in the chain.

None
Source code in src/design_patterns/behavioral/chain_of_responsibility.py
def __init__(self, successor: Optional['RequestHandler'] = None) -> None:
    """Initializes the handler with an optional successor.

    Args:
        successor (Optional[RequestHandler]): The next handler in the chain.
    """
    self.successor = successor

handle(request)

Handles the request or passes it to the next handler.

Parameters:

Name Type Description Default
request str

The request to be handled.

required

Returns:

Name Type Description
str str

The result of the request.

Source code in src/design_patterns/behavioral/chain_of_responsibility.py
def handle(self, request: str) -> str:
    """Handles the request or passes it to the next handler.

    Args:
        request (str): The request to be handled.

    Returns:
        str: The result of the request.
    """
    if self.successor:
        return self.successor.handle(request)
    return f"No handler for {request}"