Fundamentals 8 min read

Master Go Design Patterns: Practical Implementations & Best Practices

This article explores common design patterns in Go, explaining how the language’s emphasis on simplicity, composition, and interfaces influences implementations of Singleton, Factory, Strategy, Observer, and more, while providing idiomatic code examples and best‑practice recommendations such as using sync.Once, dependency injection, and context for robust, maintainable Go applications.

php中文网 Courses
php中文网 Courses
php中文网 Courses
Master Go Design Patterns: Practical Implementations & Best Practices

Design patterns are proven solutions for recurring design problems in software development. Go (Golang) offers a unique perspective on implementing these patterns due to its simplicity, efficiency, and concurrency‑friendly features.

1. Go Language and Design Patterns

Go's design philosophy emphasizes “simplicity” and “explicit over implicit”, so some classic patterns are implemented differently compared to languages like Java or C++.

Go's impact on design patterns

No inheritance, only composition → favors Composite and Strategy patterns.

Interfaces are implicit → enables flexible Dependency Injection.

Concurrency primitives (goroutine & channel) → affect Observer and Producer‑Consumer patterns.

Functions are first‑class citizens → facilitate Decorator and Strategy patterns.

2. Common Go Design Patterns and Implementations

2.1 Singleton Pattern

Ensures a class has only one instance and provides a global access point.

Typical use cases: database connections, loggers, configuration management, etc.

package singleton

import "sync"

type singleton struct {}

var instance *singleton
var once sync.Once

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

Best practice: use

sync.Once

to guarantee thread safety and avoid race conditions.

2.2 Factory Pattern

Encapsulates object creation logic, decoupling client code from concrete implementations.

Typical use cases: creating different objects based on conditions (e.g., database drivers, log storage methods).

package factory

type Database interface {
    Connect() string
}

type MySQL struct {}

func (m MySQL) Connect() string { return "MySQL connected" }

type PostgreSQL struct {}

func (p PostgreSQL) Connect() string { return "PostgreSQL connected" }

func NewDatabase(dbType string) Database {
    switch dbType {
    case "mysql":
        return MySQL{}
    case "postgres":
        return PostgreSQL{}
    default:
        return nil
    }
}

Best practice: return an interface type rather than a concrete struct to improve extensibility.

2.3 Strategy Pattern

Defines a family of algorithms and makes them interchangeable.

Typical use cases: payment method selection, swapping sorting algorithms, etc.

package strategy

type PaymentStrategy interface {
    Pay(amount float64) string
}

type CreditCard struct {}

func (c CreditCard) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via Credit Card", amount)
}

type PayPal struct {}

func (p PayPal) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via PayPal", amount)
}

type PaymentContext struct {
    strategy PaymentStrategy
}

func (p *PaymentContext) SetStrategy(strategy PaymentStrategy) {
    p.strategy = strategy
}

func (p *PaymentContext) ExecutePayment(amount float64) string {
    return p.strategy.Pay(amount)
}

Best practice: leverage Go's interface feature to switch strategies easily.

2.4 Observer Pattern

Defines a one‑to‑many dependency so that when an object’s state changes, all dependents are notified.

Typical use cases: event‑driven systems, message subscription, etc.

package observer

import "fmt"

type Observer interface {
    Update(string)
}

type Subject struct {
    observers []Observer
}

func (s *Subject) Register(o Observer) {
    s.observers = append(s.observers, o)
}

func (s *Subject) NotifyAll(msg string) {
    for _, o := range s.observers {
        o.Update(msg)
    }
}

type LogObserver struct {}

func (l LogObserver) Update(msg string) {
    fmt.Println("Log:", msg)
}

type EmailObserver struct {}

func (e EmailObserver) Update(msg string) {
    fmt.Println("Email:", msg)
}

Best practice: combine with

chan

for more efficient concurrent notification mechanisms.

3. Go Best Practices

3.1 Prefer composition over inheritance

Go does not have class inheritance, but struct embedding provides similar functionality.

type Logger struct {}

func (l Logger) Log(msg string) { fmt.Println(msg) }

type Service struct {
    Logger // composition
}

Advantages: more flexible and avoids complex inheritance hierarchies.

3.2 Use context to manage request lifecycles

Avoid goroutine leaks, support timeouts and cancellation.

func Process(ctx context.Context, data string) (string, error) {
    select {
    case <-ctx.Done():
        return "", ctx.Err()
    case result := <-doAsyncWork(data):
        return result, nil
    }
}

Best practice: use

context

in HTTP services, database queries, and similar scenarios.

3.3 Avoid global variables, adopt dependency injection (DI)

type Server struct {
    db *Database
}

func NewServer(db *Database) *Server {
    return &Server{db: db}
}

Advantages: improves testability and reduces implicit dependencies.

4. Summary

Go design patterns favor composition and interfaces rather than traditional OOP inheritance.

Common patterns such as Singleton, Factory, Strategy, and Observer have unique Go implementations.

Best practices include preferring composition, using

context

, applying dependency injection, and avoiding global state.

design patternsGoBest PracticessingletonStrategyFactoryObserver
php中文网 Courses
Written by

php中文网 Courses

php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.