Design Patterns
Chain of Responsibility

Chain of Responsibility

Chain of Responsibility Pattern: How the Avengers Handle Threats

Imagine how the Avengers deal with incoming global threats. When Nick Fury receives an alert, he doesn't immediately call in the entire team for every situation. Instead, there's a chain of escalation: local law enforcement handles minor incidents, S.H.I.E.L.D. agents tackle more significant threats, individual Avengers deal with super-powered villains, and only for world-ending crises does the entire Avengers team assemble. Each level in this chain either handles the threat or passes it up to the next level—that's the Chain of Responsibility pattern in action!

The Chain of Responsibility pattern lets you pass requests along a chain of handlers. Each handler decides either to process the request or to pass it to the next handler in the chain. It decouples senders and receivers by giving multiple objects a chance to handle a request.

The Problem: Handling Requests at the Right Level

Consider a system that processes purchase approvals in a company:

  • Employees can approve purchases up to $1,000
  • Managers can approve purchases up to $5,000
  • Directors can approve purchases up to $20,000
  • The VP needs to approve anything above $20,000

Without the Chain of Responsibility pattern, you'd likely end up with complex conditional logic:

This approach has several drawbacks:

  • The code becomes harder to maintain as rules get more complex
  • Adding new approval levels requires modifying the existing logic
  • Each approval level is tightly coupled to the others
  • It's difficult to reuse or reconfigure the approval process

Enter the Chain of Responsibility Pattern

The Chain of Responsibility pattern addresses these issues by:

  1. Defining a chain of handler objects
  2. Each handler implements a common interface
  3. Each handler decides whether to process the request or pass it to the next handler
  4. The client interacts only with the first handler in the chain
Loading diagram...

Let's refactor our purchase approval system using the Chain of Responsibility pattern:

In this implementation, each approver in the chain either handles the request or passes it to the next approver. This makes the code more maintainable, extensible, and decoupled.

Avengers Threat Response Example

Let's implement the Avengers threat response system described in the introduction:

Logging System Example

A common practical application of the Chain of Responsibility pattern is in logging systems, where different loggers handle messages based on their severity:

Event Handling in UI Example

Another common application of the Chain of Responsibility pattern is handling UI events in a widget hierarchy:

Variations of the Chain of Responsibility Pattern

1. Successor Chain

The basic form we've seen where each handler points to the next handler. This works well for linear processing.

2. Command Chain

Combining with the Command pattern, where requests are encapsulated as command objects passed along the chain.

3. Intercept Filter Chain

Common in web frameworks, where each filter intercepts the request, does pre-processing, calls the next filter, and then does post-processing.

Advantages of the Chain of Responsibility Pattern

  1. Decoupling: Senders don't need to know which handler will process their request.
  2. Dynamic Chain Configuration: The chain can be composed at runtime.
  3. Single Responsibility: Each handler focuses on a specific level or type of request.
  4. Open/Closed Principle: You can add new handlers without changing existing code.
  5. Flexibility: Request processing sequence is flexible and can be controlled.

Disadvantages of the Chain of Responsibility Pattern

  1. No Guarantee of Handling: A request might go through the entire chain and not be handled.
  2. Runtime Overhead: Requests might go through many handlers before finding the right one.
  3. Debugging Challenges: It can be harder to debug and follow the flow of execution.
  4. Potential for Circular References: If not careful, you might create a circular chain.
  5. Design Overhead: For simple cases, it might be overkill compared to direct method calls.

When to Use the Chain of Responsibility Pattern

The Chain of Responsibility pattern is ideal when:

  1. More than one object may handle a request, and the handler is not known in advance.
  2. You want to issue a request to one of several objects without specifying the receiver explicitly.
  3. The set of objects that can handle a request should be specified dynamically.
  4. You need to process different kinds of requests in various ways without hardcoding handler selection.
  5. You want to decouple senders and receivers of a request.

Remember, just like the Avengers' tiered response system that escalates threats to the appropriate level, the Chain of Responsibility pattern helps your code route requests to the right handler without the sender needing to know who will ultimately process it!