A-Real-World-Example-of-Go-Interfaces

孤独的孩子,悄悄在风中长大了。

Suppose I’m building a web app in Go. In my app, I want to send a message to my users. I can send a message to the users via email or SMS. This would be a perfect use case for interfaces.

For this hypothetical web app, I create the following main.go file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

type User struct {
Name string
Email string
}

type UserNotifier interface {
SendMessage(user *User, message string) error
}

func main() {
user := &User{"Dirk", "dirk@email.com"}

fmt.Printf("Welcome %s\n", user.Name)
}

The User struct represents a user.
You can see I created a UserNotifier of type interface that has a single function: SendMessage().
To implement this interface, I have to create a struct that implements the SendMessage() function.

1
2
3
4
5
6
7
type EmailNotifier struct {
}

func (notifier EmailNotifier) SendMessage(user *User, message string) error {
_, err := fmt.Printf("Sending email to %s with content %s\n", user.Name, message)
return err
}

As you can see, I created a new EmailNotifier struct. Then I implemented the SendMessage() method on the struct. Note that in this example, the EmailNotifier simply prints a message. In the real world, you would probably call an email API such as Mailgun.

And that’s it, the interface is now implemented.

The next thing to do is send an email to the user using the UserNotifier interface.

1
2
3
4
5
6
7
8
func main() {
user := User{"Dirk", "dirk@email.com"}
fmt.Printf("Welcome %s\n", user.Name)

var userNotifier UserNotifier
userNotifier = EmailNotifier{}
userNotifier.SendMessage(&user, "Interfaces all the way!")
}

When running this program, the SendMessage() of the EmailNotifier is called correctly.

1
2
3
4
go build -o main main.go
./main
Welcome Dirk
Sending email to Dirk with content Interfaces all the way!

Let’s implement an SMS interface as well.

1
2
3
4
5
6
7
type SmsNotifier struct {
}

func (notifier SmsNotifier) SendMessage(user *User, message string) error {
_, err := fmt.Printf("Sending SMS to %s with content %s\n", user.Name, message)
return err
}

One cool feature is we can store the notifier in the user struct. In this way, each user will have a personal notifier.

1
2
3
4
5
type User struct {
Name string
Email string
Notifier UserNotifier
}

You can then add a handy method to the User struct that notifies the user using the UserNotifier interface.

1
2
3
func (user *User) notify(message string) error {
return user.Notifier.SendMessage(user, message)
}

Finally, I created two users in the main() function and called the notify() method.

1
2
3
4
5
6
7
func main() {
user1 := User{"Dirk", "dirk@email.com", EmailNotifier{}}
user2 := User{"Justin", "bieber@email.com", SmsNotifier{}}

user1.notify("Welcome Email user!")
user2.notify("Welcome SMS user!")
}

The final result worked as expected.

1
2
3
4
go build -o main main.go
./main
Sending email to Dirk with content Welcome Email user!
Sending SMS to Justin with content Welcome SMS user!