linkChain of Responsibility

Description

Chain of Responsibility is a behavioral design pattern that allows a request to be passed along a chain of commands without having to associate the class sending the request with the class receiving it.

Real World Example

The description of this pattern is a bit complicated, so let’s imagine that we are in a store, we have just done shopping and approach the cash register. The cashier has already checked our purchases and is waiting for payment. We have several types of payment to choose from:
  • voucher-up to $20
  • credit card-up to $200
  • cash-up to $100

Code Example

Let’s create the first class that will be responsible for handling payments. To create it, we will have to provide some type of payment: card, cash or voucher. Additionaly the class implements two extra methods:
  • success-if the transaction is successful, it returns a message depending on what we paid with
  • error-shows information that the transaction failed
class PaymentHandler
  attr_accessor :successor

  def initialize(successor = nil)
    @successor = successor
  end

  def call(price)
    successor ? successor.call(price) : error
  end

  private

  def success(message)
    message
  end

  def error
    "You are poor and have nothing to pay!"
  end
end
The first way we will try to pay will be a Voucher. So let’s implement a class that will handle this payment. The voucher can be used to pay for purchases up to $20.
class VoucherPaymentHandler < PaymentHandler
  def call(price)
    price < 20 ? success('You paid by Voucher!') : super(price)
  end
end
The next step will be to implement cash handler.
class CashPaymentHandler < PaymentHandler
  def call(price)
    price < 100 ? success('You paid by Cash!') : super(price)
  end
end
And card handler.
class CardPaymentHandler < PaymentHandler
  def call(price)
    price < 200 ? success('You paid by Card!') : super(price)
  end
end
At this point, we are able to implement our chain.

payment = VoucherPaymentHandler.new(
  CashPaymentHandler.new(
    CardPaymentHandler.new
  )
)

payment.call(10) => "You paid by Voucher!"
payment.call(50) => "You paid by Cash!"
payment.call(150) => "You paid by Card!"
payment.call(420) => "You are poor and have nothing to pay!"
Depending on what we pay with, the methods return success or if they are unable to handle the payment, they pass it on until the last link in the chain, which returns an error in case of failure.
A well-prepared chain allows you to handle an endless number of implementations and allows you to place specific handler anywhere in the chain.