Design pattern - Chain of Responsibility # Chain of Responsibility ![https://refactoring.guru/images/patterns/content/chain-of-responsibility/chain-of-responsibility.png](https://refactoring.guru/images/patterns/content/chain-of-responsibility/chain-of-responsibility.png) ## What is Chain of Responsibility ### A behavioral design pattern that: - Chain of Responsibility relies on transforming particular behaviors into stand-alone objects called handlers. - 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. ## Real-world Analogy ![https://refactoring.guru/images/patterns/content/chain-of-responsibility/chain-of-responsibility-comic-1-en.png](https://refactoring.guru/images/patterns/content/chain-of-responsibility/chain-of-responsibility-comic-1-en.png) ## Problem ### An online ordering system ![https://refactoring.guru/images/patterns/diagrams/chain-of-responsibility/problem1-en.png](https://refactoring.guru/images/patterns/diagrams/chain-of-responsibility/problem1-en.png) ![https://refactoring.guru/images/patterns/diagrams/chain-of-responsibility/problem2-en.png](https://refactoring.guru/images/patterns/diagrams/chain-of-responsibility/problem2-en.png) ### The bigger the code grew, the messier it became. --- ## Solution ![https://refactoring.guru/images/patterns/diagrams/chain-of-responsibility/solution1-en.png](https://refactoring.guru/images/patterns/diagrams/chain-of-responsibility/solution1-en.png) --- ## Applicability ### Use the Chain of Responsibility pattern when ... - Your program is expected to process different kinds of requests in various ways, but the exact types of requests and their sequences are unknown beforehand. >The pattern lets you link several handlers into one chain and, upon receiving a request, “ask” each handler whether it can process it. This way all handlers get a chance to process the request. - It's essential to execute several handlers in a particular order. >Since you can link the handlers in the chain in any order, all requests will get through the chain exactly as you planned. - *A chain can be formed from a branch of an object tree.* The set of handlers and their order are supposed to change at runtime. >If you provide setters for a reference field _inside the handler classes_, you’ll be able to insert, remove or reorder handlers dynamically. --- ## Structure ![https://refactoring.guru/images/patterns/diagrams/chain-of-responsibility/structure.png](https://refactoring.guru/images/patterns/diagrams/chain-of-responsibility/structure.png) --- ## How to Implement 1. Declare the handler interface and describe the signature of a method for handling requests. Decide how the client will pass the request data into the method. >The most flexible way is to convert the request into an **object** and pass it to the handling method as an argument. 2. To eliminate duplicate boilerplate code in concrete handlers, creating an abstract base handler class, derived from the handler interface. This class should have a field for storing a reference to the next handler in the chain. >A. Consider making the class immutable. >B. You need to define a setter for altering the value of the reference field if you plan to modify chains at runtime. >C. You can also implement the convenient default behavior for the handling method, which is to forward the request to the next object unless there’s none left. Concrete handlers will be able to use this behavior by calling the parent method. 3. One by one create concrete handler subclasses and implement their handling methods. Each handler should make two decisions when receiving a request: >Whether it’ll process the request. >Whether it’ll pass the request along the chain. 5. The client may either assemble chains on its own or receive pre-built chains from other objects. In the latter case, you must implement some factory classes to build chains according to the configuration or environment settings. 6. The client may trigger any handler in the chain, not just the first one. The request will be passed along the chain until some handler refuses to pass it further or until it reaches the end of the chain. ## Pros and Cons - You can control the order of request handling, not just the first one. - _Single Responsibility Principle_. You can decouple classes that invoke operations from classes that perform operations. - _Open/Closed Principle_. You can introduce new handlers into the app without breaking the existing client code. - Some requests may not reach the end of the chain; Some requests may end up unhandled. --- ## Code example: ```ruby # The Handler interface declares a method for building the chain of handlers. It # also declares a method for executing a request. class Handler # @abstract # # @param [Handler] handler def next_handler=(handler) raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end # @abstract # # @param [String] request # # @return [String, nil] def handle(request) raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end end # The default chaining behavior can be implemented inside a base handler class. class AbstractHandler < Handler # @return [Handler] attr_writer :next_handler # @param [Handler] handler # # @return [Handler] def next_handler(handler) @next_handler = handler # Returning a handler from here will let us link handlers in a convenient # way like this: # monkey.next_handler(squirrel).next_handler(dog) handler end # @abstract # # @param [String] request # # @return [String, nil] def handle(request) return @next_handler.handle(request) if @next_handler nil end end # All Concrete Handlers either handle a request or pass it to the next handler # in the chain. class MonkeyHandler < AbstractHandler # @param [String] request # # @return [String, nil] def handle(request) if request == 'Banana' "Monkey: I will eat the #{request}" else super(request) end end end class SquirrelHandler < AbstractHandler # @param [String] request # # @return [String, nil] def handle(request) if request == 'Nut' "Squirrel: I will eat the #{request}" else super(request) end end end class DogHandler < AbstractHandler # @param [String] request # # @return [String, nil] def handle(request) if request == 'MeatBall' "Dog: I will eat the #{request}" else super(request) end end end # The client code is usually suited to work with a single handler. In most # cases, it is not even aware that the handler is part of a chain. def client_code(handler) ['Nut', 'Banana', 'Cup of coffee'].each do |food| puts "\nClient: Who wants a #{food}?" result = handler.handle(food) if result print " #{result}" else print " #{food} was left untouched." end end end monkey = MonkeyHandler.new squirrel = SquirrelHandler.new dog = DogHandler.new monkey.next_handler(squirrel).next_handler(dog) # The client should be able to send a request to any handler, not just the first # one in the chain. puts 'Chain: Monkey > Squirrel > Dog' client_code(monkey) puts "\n\n" puts 'Subchain: Squirrel > Dog' client_code(squirrel) ``` ```bash Chain: Monkey > Squirrel > Dog Client: Who wants a Nut? Squirrel: I will eat the Nut Client: Who wants a Banana? Monkey: I will eat the Banana Client: Who wants a Cup of coffee? Cup of coffee was left untouched. Subchain: Squirrel > Dog Client: Who wants a Nut? Squirrel: I will eat the Nut Client: Who wants a Banana? Banana was left untouched. Client: Who wants a Cup of coffee? Cup of coffee was left untouched. ```