Chain Of Responsibility (Pipeline) Design Pattern
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. (dofactory)
- Handler defines an interface for handling the requests and can implement the successor link (optional).
- ConcreteHandler handles requests it is responsible for and can access its successor. if the ConcreteHandler can handle the request, it does so; otherwise it forwards the request to its successor.
- Client initiates the request to a ConcreteHandler object on the chain.
- The chaining mechanism uses recursive composition to allow an unlimited number of handlers to be linked.
- If the “current” object is not available or sufficient, then it delegates to the base class, which delegates to the “next” object, and the circle of life continues.
- Multiple handlers could contribute to the handling of each request. The request can be passed down the entire length of the chain, with the last link being careful not to delegate to a “null next”.
- More than one object can handle a command
- The handler is not known in advance
- The handler should be determined automatically
- It’s wished that the request is addressed to a group of objects without explicitly specifying its receiver
- The group of objects that may handle the command must be specified in a dynamic way
- Note: The pattern is used in windows systems to handle events generated from the keyboard or mouse. Exception handling systems also implement this pattern, with the runtime checking if a handler is provided for the exception through the call stack. If no handler is defined, the exception will cause a crash in the program, as it is unhandled. .Net framework implements Chain Of Responsibility design pattern for HttpModule. In JavaEE, the concept of Servlet filters implement the Chain of Responsibility pattern, and may also decorate the request to add extra information before the request is handled by a servlet.
- Launch-and-leave requests with a single processing pipeline that contains many possible handlers allowing you to add or remove responsibilities dynamically by changing the members or order of the chain.
- An object-oriented linked list with recursive traversal.
- Chain of Responsibility simplifies object interconnections. Instead of senders and receivers maintaining references to all candidate receivers, each sender keeps a single reference to the head of the chain, and each receiver keeps a single reference to its immediate successor in the chain. So, sender will not know which object in the chain will serve its request.
- Unfortunately, the Chain doesn't guarantee that every command is handled, which makes the problem worse, since unhandled commands propagate through the full length of the chain, slowing down the application. One way to solve this is by checking if, at the end of the chain, the request has been handled at least once, otherwise we will have to implement handlers for all the possible requests that may appear.
- Broken Chain problem: Sometimes we could forget to include in the implementation of the handleRequest method the call to the successor, causing a break in the chain. The request is not sent forward from the broken link and so it ends up unhandled. However, A variation of the pattern can be made to send the request to all the handlers by removing the condition from the handler and always calling the successor. e.g. moving the code to traverse the chain into the base class keeping the request handling in a different method in the subclasses.
- As with the Observer pattern, Chain of Responsibility can make it difficult to follow through the logic of a particular path in the code at runtime.
- If new operations need to be added to the Handler, modifying the source code is required.
- Do not use Chain of Responsibility when each request is only handled by one handler, or, when the client object knows which service object should handle the request.
Pipeline or Filter pattern is similar to CoR pattern, however instead of each Concrete Handler has a reference to the Successor in the chain, There is a handler manager that does this. The flexibility of the Pipeline pattern comes from the fact that at any time, a new Handler can be injected into the pipeline through the PipelineManager.
- Chain of Responsibility, Command, Mediator, and Observer, address how you can decouple senders and receivers, but with different trade-offs. Chain of Responsibility passes a sender request along a chain of potential receivers.
- Chain of Responsibility can use Command to represent requests as objects.
- Chain of Responsibility is often applied in conjunction with Composite. There, a component’s parent can act as its successor.