Chain of Responsibility Pattern: Definition, Example, and Use Cases
Chain of Responsibility pattern lets multiple objects handle a request by passing it along a chain until one object processes it. This avoids coupling the sender of a request to its receiver and allows dynamic handling of requests.How It Works
Imagine you have a line of people, each with a different skill. When a question is asked, the first person tries to answer it. If they can't, they pass the question to the next person, and so on, until someone answers or the line ends.
In software, the Chain of Responsibility pattern works the same way. A request is sent to the first handler object. If it can handle the request, it does so. If not, it passes the request to the next handler in the chain. This continues until the request is handled or no handlers remain.
This pattern helps separate the sender of a request from its receivers, making the system flexible and easier to extend with new handlers without changing existing code.
Example
This example shows a simple chain of handlers that process support tickets based on their priority level.
class Handler: def __init__(self, successor=None): self.successor = successor def handle(self, request): handled = self.process_request(request) if not handled and self.successor: self.successor.handle(request) def process_request(self, request): raise NotImplementedError("Must provide implementation in subclass") class LowPriorityHandler(Handler): def process_request(self, request): if request['priority'] == 'low': print(f"LowPriorityHandler handled request: {request['content']}") return True return False class MediumPriorityHandler(Handler): def process_request(self, request): if request['priority'] == 'medium': print(f"MediumPriorityHandler handled request: {request['content']}") return True return False class HighPriorityHandler(Handler): def process_request(self, request): if request['priority'] == 'high': print(f"HighPriorityHandler handled request: {request['content']}") return True return False # Setup chain: low -> medium -> high handler_chain = LowPriorityHandler(MediumPriorityHandler(HighPriorityHandler())) # Requests with different priorities requests = [ {'priority': 'low', 'content': 'Password reset'}, {'priority': 'medium', 'content': 'Software installation'}, {'priority': 'high', 'content': 'System outage'}, {'priority': 'urgent', 'content': 'Data breach'} ] for req in requests: handler_chain.handle(req)
When to Use
Use the Chain of Responsibility pattern when you want to avoid coupling the sender of a request to its receiver, and when multiple objects can handle a request but the handler is not known in advance.
It is useful when you want to add or change handlers dynamically without modifying the sender or other handlers.
Real-world examples include:
- Event handling systems where events pass through multiple listeners
- Logging frameworks with multiple log levels
- Technical support systems routing tickets based on priority or type
- Middleware pipelines in web servers
Key Points
- Decouples sender and receiver by passing requests along a chain.
- Each handler decides to process or forward the request.
- Supports dynamic and flexible request processing.
- Easy to add new handlers without changing existing code.
- Can simplify complex conditional logic.