Chain of Responsibility Design Pattern
The Chain of Responsibility is a behavioral design pattern that allows an object to pass a request along a chain of potential handlers. Upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain. This pattern avoids coupling the sender of a request to its receiver, giving more than one object a chance to handle the request.
Concept:
- Handler: Defines an interface for handling requests and, optionally, implements the successor link.
- ConcreteHandler: Handles requests it is responsible for; otherwise, it forwards the request to its successor.
- Client: Initiates requests to a chain of handlers.
- Request: Represents the request to be handled.
Example:
Let's consider a scenario where we have a system for processing purchase requests, and we want to implement a Chain of Responsibility to handle different levels of approval.
Handler Interface:
# Handler Interface
class Approver
attr_reader :successor
def initialize(successor = nil)
@successor = successor
end
def process_request(request)
raise NotImplementedError, 'Subclasses must implement the process_request method'
end
end
Concrete Handlers:
# Concrete Handler: Manager
class Manager < Approver
def process_request(request)
if request.amount <= 1000
puts "Manager approves the request with amount #{request.amount}"
elsif @successor
@successor.process_request(request)
else
puts 'Request cannot be approved.'
end
end
end
# Concrete Handler: Director
class Director < Approver
def process_request(request)
if request.amount <= 5000
puts "Director approves the request with amount #{request.amount}"
elsif @successor
@successor.process_request(request)
else
puts 'Request cannot be approved.'
end
end
end
# Concrete Handler: VicePresident
class VicePresident < Approver
def process_request(request)
if request.amount <= 10000
puts "Vice President approves the request with amount #{request.amount}"
elsif @successor
@successor.process_request(request)
else
puts 'Request cannot be approved.'
end
end
end
Request Class:
# Request Class
class PurchaseRequest
attr_reader :amount
def initialize(amount)
@amount = amount
end
end
Client Code:
# Client code using the Chain of Responsibility Pattern
purchase_request = PurchaseRequest.new(8000)
manager = Manager.new
director = Director.new
vice_president = VicePresident.new
manager.successor = director
director.successor = vice_president
manager.process_request(purchase_request)
In this example:
-
Approver
is the handler interface. -
Manager
,Director
, andVicePresident
are concrete handlers. Each handler decides whether to approve the request or pass it to the next handler in the chain. -
PurchaseRequest
represents the request to be handled. - The client code sets up the chain of responsibility and initiates the request through the first handler.
This pattern allows us to create a flexible and decoupled chain of handlers where each handler can either handle the request or pass it to the next handler in the chain. It's particularly useful in scenarios where multiple objects may handle a request, and the client doesn't need to know which object will ultimately process the request.