~/Golang Interfaces and Design Patterns Explained

Mar 13, 2023


Golang interfaces are a core component in the language, enabling polymorphism and decoupling code. This article covers Golang interfaces, their usage, and common design patterns implemented in Go. Readers should know Go syntax and object oriented principles.

Interfaces Overview

An interface in Go specifies a method set. Types implementing all interface methods implicitly satisfy the interface.

Example:

1
2
3
4
5
6
7
8
9
type Reader interface {
    Read(p []byte) (n int, err error)
}

type MyReader struct{}

func (r MyReader) Read(p []byte) (int, error) {
    return 0, nil
}

Here, MyReader satisfies the Reader interface.

Why Use Interfaces

Interfaces enable abstraction, allow for mocking in tests, and promote dependency inversion.

Design Patterns with Go Interfaces

Singleton Pattern

Singleton ensures only one instance of an object exists. Go can achieve singletons using a package-level variable and sync.Once.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package singleton

import "sync"

var instance *MyType
var once sync.Once

type MyType struct{}

func GetInstance() *MyType {
    once.Do(func() {
        instance = &MyType{}
    })
    return instance
}

Factory Pattern

Factory provides a way to create objects without specifying concrete types.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
type Animal interface {
    Speak() string
}

type Dog struct{}
func (Dog) Speak() string { return "Woof" }

type Cat struct{}
func (Cat) Speak() string { return "Meow" }

func AnimalFactory(name string) Animal {
    if name == "dog" {
        return Dog{}
    }
    if name == "cat" {
        return Cat{}
    }
    return nil
}

Strategy Pattern

Strategy enables defining a family of algorithms.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
type SortStrategy interface {
    Sort([]int)
}

type BubbleSort struct{}
func (BubbleSort) Sort(nums []int) {/* implement */}

type QuickSort struct{}
func (QuickSort) Sort(nums []int) {/* implement */}

type Context struct {
    strategy SortStrategy
}

func (c *Context) SetStrategy(s SortStrategy) {
    c.strategy = s
}
func (c *Context) Sort(nums []int) {
    c.strategy.Sort(nums)
}

Decorator Pattern

Decorator adds behavior to objects.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
type Notifier interface {
    Send(msg string)
}

type EmailNotifier struct{}
func (EmailNotifier) Send(msg string) {/* ... */}

type SMSDecorator struct {
    Notifier
}

func (s SMSDecorator) Send(msg string) {
    s.Notifier.Send(msg)
    // Add SMS sending
}

Adapter Pattern

Adapter allows incompatible interfaces to work together.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
type Target interface {
    Request()
}

type Adaptee struct{}
func (a Adaptee) SpecificRequest() {/* ... */}

type Adapter struct {
    adaptee Adaptee
}

func (a Adapter) Request() {
    a.adaptee.SpecificRequest()
}

Command Pattern

Command encapsulates a request as an object.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
type Command interface {
    Execute()
}

type LightOn struct{}
func (LightOn) Execute() {/* turn light on */}

type Switch struct {
    command Command
}
func (s *Switch) Press() {
    s.command.Execute()
}

Best Practices

Conclusion

Golang interfaces enable powerful abstraction while supporting multiple design patterns. Understanding their role is essential in idiomatic and maintainable Go codebases. Refer to official Go documentation and pattern examples for deeper study.

Tags: [golang] [interfaces] [patterns]