Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
chain of responsibility example in ruby
class PurchaseApprover
# 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.
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
puts "Your request for $#{request.amount} needs a board meeting!"
end
end
class AmountApprover < PurchaseApprover
# Base class for approvers who only consider whether the request amount is
# allowable
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 CFO < PurchaseApprover
# An example of a handler that does not inherit from AmountApprover
#
# Try adding it to the chain by implementing an annual budget that tracks the
# total of all approved requests so far and whether we can afford one more.
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
# ...
end
end
class PurchaseRequest
attr_reader :amount
def initialize(number, amount, purpose)
@number = number
@amount = amount
@purpose = purpose
end
end
class CLP
def initialize(*approvers)
approvers = build_approvers(approvers)
@authority = 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
# main program
# ------------
approvers = CLP.new(Manager, Director, VicePresident, President)
loop do
$stdout.flush
puts
puts "Enter the amount to check who should approve your expenditure."
print ">> "
amount = gets.to_i
approvers.process_request PurchaseRequest.new(0, amount, 'General')
end
@malachaifrazier

This comment has been minimized.

Copy link

malachaifrazier commented Jun 8, 2015

Well done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.