Chain of Responsibility Pattern¶
Intent¶
The Chain of Responsibility pattern avoids 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.
Problem¶
When you need to process requests in a flexible way, but you don't want the sender to be coupled to specific receivers:
- Multiple objects may be able to handle a request
- The handler isn't known in advance
- The set of handlers should be specified dynamically
- You want to issue a request to one of several objects without specifying the receiver explicitly
For example, in a help system, context-sensitive help depends on which part of the application has focus. The help request should be handled by the appropriate component, but the requester shouldn't need to know which component will handle it.
Solution¶
The Chain of Responsibility pattern suggests creating a chain of handler objects. Each handler:
- Has a reference to the next handler in the chain
- Decides whether to process the request or pass it to the next handler
- Can process the request and/or pass it along the chain
- Is independent and doesn't know about other handlers (except the next one)
The request travels through the chain until a handler processes it or the chain ends.
Structure¶
Participants¶
- Handler
- Defines an interface for handling requests
- Optionally implements the successor link
- In our example:
Handlerabstract class
- ConcreteHandler
- Handles requests it is responsible for
- Can access its successor
- If it can handle the request, does so; otherwise forwards it
- In our example:
ConcreteHandlerA,ConcreteHandlerB,ConcreteHandlerC
- Client - Initiates the request to a ConcreteHandler in the chain - Doesn't know which handler will process the request
Implementation¶
Handler Base Class¶
Concrete Handlers¶
Usage Example¶
Applicability¶
Use the Chain of Responsibility pattern when:
- More than one object may handle a request - The handler isn't known a priori - Example: Event handling in GUI where parent widgets can handle events
- You want to issue a request to one of several objects without specifying the receiver explicitly - Sender doesn't need to know which object handles the request - Example: Help system where context determines handler
- The set of objects that can handle a request should be specified dynamically - Can build/modify the chain at runtime - Example: Middleware pipeline in web frameworks
- You want to decouple sender and receiver - Sender doesn't need to know about receivers - Example: Logging with different severity levels
Consequences¶
Benefits¶
-
Reduced coupling - Sender doesn't need to know which object handles the request - Objects only know about their successor - Easy to add/remove responsibilities
-
Added flexibility in assigning responsibilities - Can change chain at runtime - Can add new handlers without modifying existing code - Can reorder handlers dynamically
-
Single Responsibility Principle - Each handler focuses on one type of request - Separation of concerns
-
Open/Closed Principle - Can introduce new handlers without changing existing ones - Extend functionality without modification
Drawbacks¶
-
Receipt isn't guaranteed - Request may reach end of chain without being handled - Need to handle this case (default handler or error)
-
Hard to observe runtime characteristics - Difficult to debug which handler processes which request - Chain traversal can be hard to trace
-
Performance concerns - Request may traverse many handlers - Each handler in chain adds overhead - Long chains can impact performance
-
Chain configuration - Setting up chain correctly can be complex - Need to ensure chain is properly configured
Chain Variations¶
1. Pure Chain¶
Each handler either: - Processes the request (and stops) - Passes it to the next handler
2. Impure Chain¶
Handlers can: - Process and pass to next - Partially process and pass remainder - Process, modify, and pass
3. Conditional Chain¶
Handler decides based on condition:
Real-world Examples¶
1. Support Ticket System¶
- Handlers: Basic Support → Technical Support → Manager Support
- Request: Support tickets with different severity levels
- Escalation: Ticket moves up chain based on complexity
2. GUI Event Handling¶
- Handlers: Button → Panel → Window → Application
- Request: Mouse clicks, key presses
- Bubbling: Event propagates from child to parent until handled
3. Web Middleware¶
- Handlers: Authentication → Logging → Compression → Routing
- Request: HTTP requests
- Processing: Each middleware processes or passes request
4. Logging Framework¶
- Handlers: Debug Logger → Info Logger → Error Logger
- Request: Log messages with severity levels
- Filtering: Each logger handles messages at its level
5. Exception Handling¶
- Handlers: Try-catch blocks at different levels
- Request: Exceptions
- Propagation: Exception moves up stack until caught
6. ATM Cash Dispensing¶
- Handlers: $100 Dispenser → $50 Dispenser → $20 Dispenser
- Request: Withdrawal amount
- Distribution: Each dispenser handles what it can, passes remainder
7. Email Spam Filter¶
- Handlers: Blacklist → Bayesian Filter → Content Filter
- Request: Incoming emails
- Filtering: Each filter checks and may block or pass
Implementation Considerations¶
1. Successor Chain¶
Options for implementing the chain:
2. Representing Requests¶
Options:
3. Default Handler¶
Provide a default handler at chain end:
4. Connecting Successors¶
Manual connection:
Chain builder:
5. Breaking the Chain¶
Handlers can break the chain:
Chain of Responsibility vs Similar Patterns¶
Chain of Responsibility vs Decorator¶
| Aspect | Chain of Responsibility | Decorator |
|---|---|---|
| Intent | Handle request in chain | Add responsibilities |
| Processing | One handler processes | All decorators process |
| Order | Stops when handled | All layers execute |
| Success | May or may not be handled | Always processes |
Chain of Responsibility vs Composite¶
| Aspect | Chain of Responsibility | Composite |
|---|---|---|
| Structure | Linear chain | Tree structure |
| Propagation | Stops when handled | Propagates to all children |
| Purpose | Find handler | Treat individuals and groups uniformly |
Chain of Responsibility vs Command¶
| Aspect | Chain of Responsibility | Command |
|---|---|---|
| Focus | Who handles request | Encapsulate request |
| Connection | Handlers linked | Commands independent |
| Receiver | Dynamic (traverses chain) | Fixed (bound to command) |
Sample Output¶
Key Takeaways¶
- Chain of Responsibility decouples sender from receiver of a request
- Multiple objects can handle a request; handler determined at runtime
- Request travels through chain until handled or chain ends
- Each handler decides to process or pass request to next handler
- Easy to add/remove handlers without affecting others (OCP)
- Each handler has single responsibility (SRP)
- Request may not be handled if no suitable handler exists
- Commonly used in GUI event handling, middleware, logging, and exception handling
- Can be pure (one handler processes) or impure (multiple handlers process)
- Trade-off: Flexibility and loose coupling vs guaranteed handling and debuggability