You’ve got a big if/else
statement to decide how to handle some logic in one of your classes.
if order.currency == 'gdp' && order.amount < 500
PayPalPayment.process(order)
elsif order.currency == 'gdp' && order.amount > 500
StripePayment.process(order)
elsif order.currency == 'usd'
SquarePayment.process(order)
elsif order.currency == 'eur'
PayPalPayment.process(order)
elsif order.currency == 'zar'
WesternUnionPayment.process(order)
elsif # more examples
...
end
The sender object (the class with the above code in) is completely coupled to the receiver classes (e.g. StripePayment
, PayPalPayment
etc…)— it knows all possible handlers and the logic that decides which handler to use. If the handler changes (e.g. one is removed, added or updated), or if the logic to determine which handler to use changes (e.g. Stripe introduces a limit) you risk breaking your application while making the change to this already bloated class, not to mention it’s increasingly difficult for someone to understand what you’re program is doing.
StripePayment
, PayPalPayment
) but you won’t know which one to use until runtime (i.e. when you know which currency is being used to make the purchase).The chain of responsibility pattern is a behavioural design pattern. Behavioural design patterns are concerned with algorithms and the assignment of responsibilities between objects including the patterns of communication between them. The other categories of design pattern are creational* and structural**.
This patterns avoids 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.
Instead of a big if/else
statement, we can create a chain of handlers. The request will go through each handler in order (each link in the chain passing it to the next if it cannot resolve the request) until it finds a handler that can resolve it.
Okay so let’s get into the code. We are going to create a base handler, that all handlers will inherit from
class BaseHandler
attr_reader :successor
def initialize(successor = nil)
@successor = successor
end
def call(transaction)
if can_handle?(transaction)
handle(transaction)
else
successor.call(transaction)
end
end
def handle(_transaction)
raise NotImplementedError, 'Each handler should respond to handle and can_handle? methods'
end
end
Now we need to create the chain. We can start with the first handler, which we want to be the most specific handler.
#ruby #design-patterns #coding #learning-to-code #software-development #visual studio code