130 lines
2.9 KiB
Ruby
130 lines
2.9 KiB
Ruby
# 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')
|