DeployU
Interviews / Backend Engineering / What are interfaces and how are they used in Go?

What are interfaces and how are they used in Go?

conceptual Core Concepts Interactive Quiz Code Examples

The Scenario

You are a backend engineer at a fintech company. You are building a new service that needs to be able to process payments from a variety of different payment providers, such as Stripe, PayPal, and Braintree.

You need to find a way to write your code so that it is not tightly coupled to any specific payment provider.

The Challenge

Explain what interfaces are in Go and how you would use them to solve this problem. What are the key benefits of using interfaces?

Wrong Approach

A junior engineer might try to solve this problem by writing a separate function for each payment provider. This would be a very verbose and difficult to maintain solution.

Right Approach

A senior engineer would know that interfaces are the perfect tool for this job. They would be able to explain what interfaces are and how to use them to write code that is not tightly coupled to any specific payment provider.

Step 1: Understand What Interfaces Are

An interface is a collection of method signatures. A type implements an interface by implementing all the methods in the interface.

In Go, interfaces are implemented implicitly. This means that you do not need to explicitly declare that a type implements an interface.

Step 2: Define an Interface

Here’s how we can define an interface for our payment providers:

type PaymentProvider interface {
    Pay(amount float64) error
}

Step 3: Implement the Interface

Next, we can implement the interface for each of our payment providers:

type Stripe struct{}

func (s Stripe) Pay(amount float64) error {
    // ... (Stripe payment logic) ...
    return nil
}

type PayPal struct{}

func (p PayPal) Pay(amount float64) error {
    // ... (PayPal payment logic) ...
    return nil
}

Step 4: Use the Interface

Finally, we can write a function that takes the PaymentProvider interface as an argument:

func processPayment(p PaymentProvider, amount float64) error {
    return p.Pay(amount)
}

// ...

stripe := Stripe{}
processPayment(stripe, 100.0)

paypal := PayPal{}
processPayment(paypal, 100.0)

By using an interface, we can write code that is not tightly coupled to any specific payment provider. This makes our code more flexible and easier to maintain.

Practice Question

You have a function that takes an `io.Reader` as an argument. Which of the following can you pass to this function?