Design patterns are just tools that help us constructing a software.
Template Method pattern
In the Template Method pattern, we create a skeletal class and it is basis for various subclasses or concrete classes. Within in the skeletal class, there are abstract methods, which in turn, will be overridden by the methods of subclasses.
Let's take an example of simple payment system,
Usually, the payment process has the same flow regardless of the provider we use.
The flow of our example payment system is,
- Authenticate the merchant (our application) with the provider
- Send the User's card data with the amount of order
- Receive confirmation or error from the provider
If we want to implement this payment algorithm, it would look like,
class StripePayment
def initialize card, amount
@api_key = ENV['STRIPE_API_KEY']
@card = card
@amount = amount
end
def process_payment!
authenticate_merchant && make_payment
end
def authenticate_merchant
begin
return true if Stripe::Merchant.authenticate @api_key
rescue Stripe::MerchantError => e
Rails.logger.error "Cannot establish connection between Merchant and Provider."
return false
rescue Stripe::ProviderUnreachable => e
Rails.logger.error "Provider unreachable."
return false
end
end
def make_payment
begin
return true if Stripe::Payment.process! @api_key, @card, @amount
rescue Stripe::PaymentUnprocessable => e
Rails.logger.error "Payment unprocessable, try again."
return false
end
end
end
So this looks all fine. We can instantiate a new StripePayment object, pass the card object and the amount of the order as parameters in the initializer and call the process_payment! method on the object to execute the payment. For a successful payment, we need the Merchant (our web application) to successfully authenticate with the payment provider (Stripe) and then the credit card to be charged the total of the order. If any of these two fail, the payment wont be processed.
What about PayPal payment?
Well, we will think to add a new class and which defines the same flow. And what if we would like to add another payment options like credit card payment, or some other.
So, here comes the idea of Template Method pattern.
class BasePayment
def initialize card, amount
@card = card
@amount = amount
end
def process_payment!
authenticate_merchant && make_payment
end
def authenticate_merchant
raise NotImplementedError.new "authenticate_merchant"
end
def make_payment
raise NotImplementedError.new "make_payment"
end
end
class StripePayment < BasePayment
def authenticate_merchant
begin
return true if Stripe::Merchant.authenticate ENV['STRIPE_API_KEY']
rescue Stripe::MerchantError => e
Rails.logger.error "Cannot establish connection between Merchant and Provider."
return false
rescue Stripe::ProviderUnreachable => e
Rails.logger.error "Provider unreachable."
return false
end
end
def make_payment
begin
return true if Stripe::Payment.process! ENV['STRIPE_API_KEY'], @card, @amount
rescue Stripe::PaymentUnprocessable => e
Rails.logger.error "Payment unprocessable, try again."
return false
end
end
end
class PaypalPayment < BasePayment
def authenticate_merchant
begin
return true if Paypal::Account.authenticate ENV['PAYPAL_API_KEY']
rescue Paypal::NotAuthenticated => e
Rails.logger.error "Cannot establish connection between Merchant and Provider."
return false
rescue Paypal::NotFound => e
Rails.logger.error "Provider unreachable."
return false
end
end
def make_payment
begin
return true if Paypal::Payment.create! ENV['PAYPAL_API_KEY'], @card, @amount
rescue Paypal::UnprocessablePayment => e
Rails.logger.error "Payment unprocessable, try again."
return false
end
end
end
Singleton pattern
Observer pattern
Decorator pattern
Factory pattern
What are the differences between procs and lambdas?
Before getting into the differences, they are very similar and both are Proc objects.
proc = Proc.new { puts "Hello world" }
lam = lambda { puts "Hello World" }
proc.class # returns 'Proc'
lam.class # returns 'Proc'And the differences are,
- Lambda is very strict about the no.of arguments passed. It throws exception when incorrect arguments. But Proc does not complain abut the no.of arguments passed, it just returns nil.
lam = lambda { |x| puts x } # Creates a lambda that takes one argument and prints it
lam.call(2) # It executes and prints 2
lam.call # ArgumentError: Wrong number of arguments (0 for 1)
lam.call(2,3) # ArgumentError: Wrong number of arguments (2 for 1)proc = Proc.new { |x| puts x } # Creates a lambda that takes one argument and prints it
proc.call(2) # It executes and prints 2
proc.call # Returns nil
proc.call(1,2,3) # It executes and prints 1. And it forgets about the extra arguments- Lambdas and Procs treat the
returnstatement differently. If there is a return statement mentioned in the lambda, it continues to execute the rest of the code. But in Proc, the return statement will return the result for entire method.
def lamda_test
lam = lambda { return }
lam.call
puts "Hello world!"
end
lamda_test # Calling this method will print "Hello world!" (i.e the flow control continues to execute after the lambda block also)def proc_test
proc = Proc.new { return }
proc.call
puts "Hello world!"
end
proc_test # Calling this method will print nothing (i.e the flow control terminates within the Proc block only.)PS: Both proc and lambda are the methods defined in the Kernal module