Go Interfaces - Simplifying Code and Boosting Flexibility

Go Interfaces - Simplifying Code and Boosting Flexibility

Go interfaces allow you to define object behavior using a set of method signatures, providing a flexible and scalable way to write code. They promote abstraction and code reuse, making it a key aspect of Go programming.

Here are some typical Go interface examples.

  1. io.Reader and io.Writer: these interfaces provide a standard way to read from and write to sources such as files, network connections, or in-memory buffers.
  2. error: a built-in interface that represents an error condition in Go. Functions can return an error value, and clients can check whether an error occurred by checking if the error is nil.
  3. sort.Interface: this interface defines the methods that a type needs to implement to be sortable by the standard library's sorting functions.
  4. http.Handler: this interface is used by the net/http package to represent HTTP handlers. It defines a single method, ServeHTTP, which takes an http.ResponseWriter and an http.Request as arguments.
  5. fmt.Stringer: this interface provides a standard way to represent an object as a string. The fmt package's Print and Println functions check if an object implements this interface and call its String method to get a string representation of the object.

These are just a few examples; you can create your own custom interfaces to meet the requirements of your particular use case.

Imagine you have a program that can send notifications via email, SMS, or push notifications. You can define an interface called Notifier that defines a method such as Notify, and then have each of your notification types implement that interface. This allows you to write a generic function that can send notifications using any type of notifier.

package main

import "fmt"

type Notifier interface {
	Notify(message string)
}
type EmailNotifier struct {
	email string
}
func (e EmailNotifier) Notify(message string) {
	fmt.Printf("Sending email to %s: %s", e.email, message)
}
type SMSNotifier struct {
	phoneNumber string
}
func (s SMSNotifier) Notify(message string) {
	fmt.Printf("Sending SMS to %s: %s", s.phoneNumber, message)
}
type FCMNotifier struct {
	deviceToken string
}
func (f FCMNotifier) Notify(message string) {
	fmt.Printf("Sending fcm notification to %s: %s", f.deviceToken, message)
}
func main() {
	var n Notifier
	n = EmailNotifier{"john.doe@example.com"}
	n.Notify("Important message")
	n = SMSNotifier{"555-123-4567"}
	n.Notify("Urgent message")
	n = FCMNotifier{"some-fcm-device-token"}
	n.Notify("Info message")
}

Try it on playground

Suppose you have a program that logs data to various destinations, such as a file, the console, or a database. You can create a Logger interface that defines methods like Log and Close, and then have each of your logger types implement that interface. This enables you to create generic logging functions capable of writing to any type of logger.

package main

import (
	"errors"
	"fmt"
	"log"
	"os"
)

type Logger interface {
	Log(message string)
	Close()
}

// File logger
type FileLogger struct {
	file *os.File
}

func (l *FileLogger) Log(message string) {
	l.file.WriteString(message)
}

func (l *FileLogger) Close() {
	l.file.Close()
}

func NewFileLogger(filename string) (*FileLogger, error) {
	file, err := os.Create(filename)
	if err != nil {
		return nil, err
	}

	return &FileLogger{file}, nil
}

// Console logger
type ConsoleLogger struct{}

func (l *ConsoleLogger) Log(message string) {
	fmt.Println(message)
}

func (l *ConsoleLogger) Close() {}


// Logger factory 
func NewLogger(l string) (Logger, error) {
	if l == "file" {
		return NewFileLogger("/tmp/app.log")
	}

	if l == "console" {
		return &ConsoleLogger{}, nil
	}
	return nil, errors.New("There are only file and console loggers available")
}

func main() {
	logger, err := NewLogger("console")
	if err != nil {
		log.Println(err)
		return
	}
	logger.Log("Starting application...")
	logger.Log("Processing input...")
	logger.Log("Shutting down application...")
	logger.Close()
}

Try it on playground