programming-examples/ruby/Patterns/chain_of_responsibility.rb

130 lines
2.9 KiB
Ruby
Raw Permalink Normal View History

2019-11-15 12:59:38 +01:00
# 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.
# Implements the chain of responsibility pattern. Does not know anything
# about the approval process, merely whether the current handler can approve
# the request, or must pass it to a successor.
class PurchaseApprover
attr_reader :successor
def initialize(successor)
@successor = successor
end
def process_request(request)
if approve_request request
return
elsif successor
successor.process_request request
else
deny_request request
end
end
# This may be overridden by a handler if it wants to provide a custom action
# when it is the last member of the chain
def deny_request request
# Implementation
end
end
# Parts of approving chain
# Base class for approvers who only consider whether the request amount is allowable
class AmountApprover < PurchaseApprover
BASE = 500
def approve_request(request)
if request.amount < self.class::ALLOWABLE
print_approval request
return true
else
return false
end
end
end
class Manager < AmountApprover
ALLOWABLE = 10 * BASE
def print_approval(request)
puts "Manager will approve $#{request.amount}"
end
end
class Director < AmountApprover
ALLOWABLE = 20 * BASE
def print_approval(request)
puts "Director will approve $#{request.amount}"
end
end
class VicePresident < AmountApprover
ALLOWABLE = 40 * BASE
def print_approval request
puts "VicePresident will approve $#{request.amount}"
end
end
class President < AmountApprover
ALLOWABLE = 60 * BASE
def print_approval(request)
puts "President will approve $#{request.amount}"
end
end
class ChiefFinancialOperations < PurchaseApprover
# An example of a handler that does not inherit from AmountApprover
def approve_request(request)
if within_annual_budget? request
puts "CFO will approve $#{request.amount}"
return true
else
return false
end
end
def within_annual_budget?(request)
# Implementation
end
end
class PurchaseRequest
attr_reader :amount, :amount, :purpose
def initialize(number, amount, purpose)
@number = number
@amount = amount
@purpose = purpose
end
end
# Realization of pattern class
class CLP
def initialize(*approvers)
@authority = build_approvers(approvers).first
end
def process_request(request)
@authority.process_request request
end
private
def build_approvers(approver_classes)
[].tap do |approvers|
approver_classes.reverse.inject(nil) do |successor, approver|
approver.new(successor).tap {|approver| approvers.unshift approver }
end
end
end
end
# Usage
approvers = CLP.new(Manager, Director, VicePresident, President)
approvers.process_request PurchaseRequest.new(0, amount, 'General')