Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save e-jambon/4dc7817e3b3d12e022c59a9f78798188 to your computer and use it in GitHub Desktop.
Save e-jambon/4dc7817e3b3d12e022c59a9f78798188 to your computer and use it in GitHub Desktop.
Ruby

Ruy Design patterns

  • 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,

  1. Authenticate the merchant (our application) with the provider
  2. Send the User's card data with the amount of order
  3. 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment