Understanding Golang Interfaces — A Simple Explanation
An interface has many definitions and applications in the Go programming language. Generally, we can say an interface defines the behavior of a given type, and it is open to adaptation by any type. The best way to look at interfaces is to do so in the context of your existing projects/ideas and to apply them in areas where it feels natural to do so. In this article, we shall be exploring what that means for you and how you can start using it right away.
Definition
We can see interfaces as open systems. It defines the set of behaviors for any type, and you can become an implementer of such an interface simply by defining the behaviors listed in the interface.
Interface as a contract
A contract is basically an agreement between two or more parties and for a contract to be respected, every player must give or receive what they agreed upon.
In the context of programming, an interface can be a contract between and function and its caller that a certain parameter must meet certain requirements before it can be used. It is now the caller’s responsibility to provide an argument that satisfies that interface, and that of the function to do its job if that interface is satisfied. Here is a quick example:
func MakeSound(singer SoundMaker) {}
In this function, we want to MakeSound(). To MakeSound(), we need a SoundMaker, and anything qualifies as a sound maker if it can produce sound. Now, SoundMaker does not have to be a real (concrete) type. It could belong to any type, but if it can do what a SoundMaker is expected to do, it will be treated as a SoundMaker.
Let’s elaborate with code:
package main
import "fmt"
type Piano struct {
Brand string
Model string
}
type Guitar struct {
Brand string
Type string
}
type SoundMaker interface {
MakeSound(string)
}
func (p Piano) MakeSound(note string) {
fmt.Printf("The piano is producing a %s sound\n", note)
}
func (g Guitar) MakeSound(note string) {
fmt.Printf("The guitar is producing a %s sound\n", note)
}
func Play(instrument SoundMaker, note string) {
instrument.MakeSound(note)
}
func main() {
piano := Piano{
Brand: "Yamaha",
Model: "PSR-E333",
}
guitar := Guitar{
Brand: "Fender",
Type: "Electric",
}
// play the piano
Play(piano, "do")
Play(piano, "re")
// play the guitar
Play(guitar, "fa")
Play(guitar, "la")
}
From the code above, we see a simple example of how interfaces are used to make applications robust. Our target here is the Play() function. We want any sound-producing device to be able to call it, and we achieve that by means of an interface. We have two instruments (structs) here: the Piano and the Guitar. We also have the interface SoundMaker which as discussed earlier, is a contract that specifies what a particular struct instrument should be able to do, in this case, MakeSound.
Then, we define the Play function. It accepts the SoundMaker interface, and now we are confident that whatever passes through this function can make a sound. That is why we can call MakeSound and get our music going. From the main method, you can see everything in action, with the Play method to play any instrument we choose.
The purpose of this short explanation is to provide you with an intuitive understanding of how interfaces work and can give you a solid foundation for the more advanced applications of interfaces. In a future post, I will be sharing my favorite way of structuring my Golang web applications and I extensively use interfaces there.
Thanks for reading this. Please like and share with whoever might need this.