Adapter Design Pattern is one of the most intuitive design patterns. We can very easily figure out why and where it is needed. In this article, we will try to build an intuition for it and try to implement the Adapter Design Pattern in Golang.

Let us try building the intuition behind this Design Pattern. Most of us might remember the time before the Smartphone era when Nokia used to rule the Indian Market. Just like today, we have a Type C and a normal version of chargers, at that time we used to have a thick pin and thin pin charger.  And anytime you visit your relatives or friends and forget your charger, you were never sure you will get the type of charger you need in their house. But to save the day, we had adapters , a tiny wire that could convert thick pin into thin pin and vice-versa.

For those who might not be able to connect with this example, let me give you another. Throughout the world, the type and size of Power Sockets change.  You might not be able to plug in the device you use in India in a power socket in Malaysia. Again to save the day, we have adapters. They just interface your plug and connect to the power socket.

This is precisely what an adapter design pattern in golang does. It converts an interface of a class to a form which the client expects, without changing the underlying behaviour and logic of that interface. The Adapter Design Pattern is used to enable the adoption of two incompatible interfaces such that their implementation remains untouched. The catch here is, the interface might be incompatible, but the inner behaviour should suit the need and complement each other.

Just like the ower adapter for a different type of power sockets. The internal behaviour is the same, the power socket is a point where you connect to get electrical supply and power plug is something that connects to the socket and let the electricity flow to the device. Only the shape/size is incompatible, the internal function is still complementing each other.

Use Case:

In the last section, we discussed what the adapter pattern does. In this section let us try to find out, what might the use cases be.
Let us take an example of a shopping cart, to be more specific, check out part of the cart. When the cart was first made, some niche payment methods like net-banking and cards were supported. This system worked on bank account details and card numbers. But with the advent of digital wallets and UPI, more and more payment methods had to be added. Now here is the catch, most of the times digital wallets expose an API or an endpoint. Majority of the wallets work on Mobile Numbers and emails and the bank accounts or card numbers are of no use to them.

How will then the shopping cart incorporate these payment methods?
They cannot write different versions of Checkout feature for all these methods. It will be clumsy and prone to a lot of bugs. What else can we do?? We can make use of adapters. For every payment method, we can write an adapter for it.  The Checkout function instead of directly calling the payment method with exactly the details in needs will call the payment method adapter with just the information it has. The adapter fetches the details that are unique to that payment type and calls the actual payment method and relays the response back to the checkout function.

 

Implementation:

Let us now try to implement the adapter design pattern in Golang.  We will try to code the use case discussed in the section above. We have two payment methods, Paypal and BankAccount.  Let us see what the payment methods look like :

package bank

import (
   "fmt"
   "time"
)
func  ProcessPayment(fromAccount int, toAccount int, amount int) error {
   fmt.Printf("Transfered %d  from %d to %d at %v via bank transfer", amount, fromAccount, toAccount, time.Now().String())
   return nil
}

package paypal

import (
   "fmt"
)

func  Send(senderMobile, receiverMobile string, amount int) error {
   fmt.Printf("Send %d  from %s to %s via paypal transfer", amount, senderMobile, receiverMobile)
   return nil
}

 

As you can see,  BankAccount has exposed a function that expects accountNumbers of the sender and receiver. The function exposed by PayPal expects the mobile number of two parties.
And in the checkout system, we have the emailID of both the parties.

Let us now look at the adapters:


package adapters

import (
   "github.com/DesignPatterns/structural/adapter/bank"
   "math/rand"
)

type BankAdapter struct {}

func (b *BankAdapter) Send(fromEmail string, toEmail string, amount int) error {
   fromAccount := findAccountByEmail(fromEmail)
   toAccount := findAccountByEmail(toEmail)
   return bank.ProcessPayment(fromAccount,toAccount,amount)
}

func findAccountByEmail(email string) int {
   //This will query the data store to return the Account Number
   return rand.Int()
}

type Paypal_Adapter struct {}

func (p *Paypal_Adapter) Send(fromEmail string, toEmail string, amount int) error {
   fromMobile := findMobileByEmail(fromEmail)
   toMobile := findMobileByEmail(toEmail)
   return paypal.Send(fromMobile,toMobile,amount)
}
func findMobileByEmail(email string) string{
   //This will query the data store to return the Account Number
   return "9877896547" // a random phone number
}


The bank adapter takes in the email and fetches bank account from it and then calls the internal function exposed by Bank. The Paypal adapter fetches the phone number from the emailIDs and then call the internal Paypal function.

Let us now look at how to use this in checkout function.

package main

import (
   "fmt"
   "github.com/DesignPatterns/structural/adapter/adapters"
)

type PaymentMethod interface {
   Send(fromEmail string, toEmail string, amount int) error
}

type Account struct {
   Email string
   Method PaymentMethod
}
func main() {
   account1 := &Account{
      Email:  "unishubh1@gmail.com",
      Method: &adapters.BankAdapter{},
   }

   account2 := &Account{
      Email:  "smartscribs@gmail.com",
      Method: &adapters.Paypal_Adapter{},
   }

   err := account1.Pay(account2, 10)
   if err!= nil {
      fmt.Println("An error occurred")
   }
}
func (a *Account) Pay(receiver *Account,amount int) error {
   fromEmail := a.Email
   toEmail := receiver.Email

   return a.Method.Send(fromEmail,toEmail,amount)
}

 

Let us debunk what is happening here. First of all, we have defined an interface type Payment Method. If you see the code of adapters, they satisfy the payment method interfaces. Hence, we never call the payment method directly, we call it’s adapter. Then we have two structs, depicting two users each having their email and a Payment Method which they chose.
We just hit the primary Pay function which automatically calls the Send method of the payment type selected and the developer is not concerned about what actual parameters the payment.
You can find the complete working code here.

This was all about the Adapter Design Pattern in Golang. This a very widely used pattern, because almost all the organisations have a lot of legacy code and they want to incorporate new functionality to it. Rewriting the whole legacy code is not an option, hence these organisations make use of the Adapter Design Pattern.
If you like this article you can also go through over discussion and implementation of other design pattern articles here.


0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *

Wordpress Social Share Plugin powered by Ultimatelysocial
error

Enjoy this blog? Please spread the word :)