In software design, the Chain of Responsibility pattern is a behavioral design pattern that allows a client object to pass a request along a chain of potential handlers until one of them handles the request. This pattern promotes the decoupling of sender and receiver, offering flexibility in assigning responsibilities to different objects. Originally described by the Gang of Four, the Chain of Responsibility pattern can be particularly useful in scenarios where multiple objects can handle a request, but the specific handler isn't known in advance.
As originally described by the Gang of Four, the Chain of Responsibility pattern involves the following components:
So a Client invokes the operation
on a handler, which probably has static type Handler Interface (Operation
) by in reality is a Concrete Handler (OperationA
, OperationB
). The handler then processes the operation itself, or delegates to the next
handler, and so on until either the operation is processed successfully, or the end of the chain is reached.
A classic example of this pattern is a user authentication system, where authentication requests are passed through a chain of handlers until either one of the handlers successfully authenticates the user and returns a session, or the end of the chain is reached and the user is not authenticated.
One defining characteristic of the Chain of Responsibility pattern in its original incarnation is that the caller receives feedback about the processing of the operation
in the form of a return value.
The Collaborative variation involves the same components and relationships as the Competitive variation, but the handlers are called according to a different call protocol. Where the Competitive variation returns the result from the first handler in the chain that produces one, the Collaborative version instead returns the combined result from all handlers in the chain. So whereas Concrete Handlers in the Competitive variation return the result immediately if they are able to process the operation
successfully, or otherwise delegate to the next Handler, Handlers in the Collaborative variation always delegate to the next handler, and then modify the result from the delegate before returning the modified response.
A classic example of this pattern is text formatting systems, where each handler performs specific text formatting tasks on a tree representation of a document.
The Consumer variation involves the same components and relationships as the Collaborative variation and even uses the same call protocol, but it doesn't return a value. The caller initiates the request not for the result, since the handlers produce none, but rather for the side effects of the invocation and for the mutations the handlers apply to the inputs.
A classic example of this pattern is HTTP request filtering, where each handler can inspect the request and make changes as desired.
While similar, this variation of the Chain of Responsibility pattern differs from the Observer pattern in that in the Chain of Responsibility pattern, the Client gets feedback from the invocation, whereas in the Observer pattern, the Client does not.
The Master Object variation introduces a new component, the Master Object, which is responsible for managing the chain of handlers and the call protocol:
This variation decouples the Handler Interface from the manner of its invocation. Because the Master Object implements the Handler Interface, the pattern still promotes the decoupling of the sender and the receiver. In most cases, Concrete Handlers don't have to know they're part of a chain, and the Client doesn't have to know it's using a chain.
This variation can be used to simplify any of the above variations.
The Chain of Responsibility pattern as originally described by the Gang of Four still provides a powerful way to decouple senders and receivers of requests. However, modern real-world applications often demand variations to fit specific requirements. By introducing a master object to control the call order or changing the call protocol, developers can create flexible and adaptable systems that leverage the strengths of both competitive and collaborative handling approaches while reducing coupling and minimizing incremental complexity.