Chain of Responsibility Design Pattern in Ruby

1. Definition

The Chain of Responsibility Design Pattern provides a mechanism to decouple the sender from the receiver by allowing more than one object to handle a request. It creates a chain of receiver objects that can either handle a specific request or pass it to the next object in the chain.

2. Problem Statement

Imagine you have multiple objects that can process a particular request, but you don't know beforehand which object should handle the request, or you want to let these objects decide amongst themselves who should handle it. Directly linking the sender to each receiver can make the system tightly coupled and complex.

3. Solution

The Chain of Responsibility pattern provides a chain of loosely coupled objects, where each object can decide either to process the request or pass it down the chain. This way, the system becomes more modular and easier to maintain.

4. Real-World Use Cases

1. Event propagation in GUI libraries, where events can be handled at different levels (e.g., button, form, window).

2. Middleware in web frameworks, where each middleware can process or modify the request/response or pass it down.

3. Input validation or processing pipelines.

5. Implementation Steps

1. Define a handler interface that specifies a method to set the next handler and another method to handle the request.

2. Concrete handlers implement this interface and either handle the request or pass it to the next handler in the chain.

3. Clients then create the chain and send the request to the first handler.

6. Implementation in Ruby

# Step 1: Handler interface
class Handler
  attr_accessor :next_handler
  def initialize
    @next_handler = nil
  end
  def handle(request)
    return @next_handler.handle(request) if @next_handler
    nil
  end
end
# Step 2: Concrete handlers
class FirstHandler < Handler
  def handle(request)
    return "FirstHandler: I'll handle the request!" if request == "Request1"
    super(request)
  end
end
class SecondHandler < Handler
  def handle(request)
    return "SecondHandler: I got this!" if request == "Request2"
    super(request)
  end
end
# Step 3: Client code
first = FirstHandler.new
second = SecondHandler.new
first.next_handler = second
puts first.handle("Request1")
puts first.handle("Request2")

Output:

FirstHandler: I'll handle the request!
SecondHandler: I got this!

Explanation:

1. Handler provides a generic interface with a mechanism to set the next handler and a method to handle the request.

2. FirstHandler and SecondHandler are concrete handlers. They check if they can handle the request. If not, they pass it to the next handler.

3. In the client code, a chain is formed with FirstHandler and SecondHandler. When a request is sent to FirstHandler, it either handles it or passes it down to SecondHandler.

7. When to use?

Use the Chain of Responsibility pattern when:

1. Multiple objects can handle a request, and you want to decouple senders from receivers.

2. You want to allow an object to handle the request or pass it down the chain dynamically.

3. You want to maintain loose coupling and modular structure in the system.


Comments