Created
February 20, 2015 21:30
-
-
Save martindemello/7231bf0f407ca428b509 to your computer and use it in GitHub Desktop.
chain of responsibility example in ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Well done.