Design Patterns in Go with Examples for Developers

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Design Patterns in Go (Golang) with Code Examples



Design patterns are proven solutions to common development problems. In Go, with its concise syntax and lack of classical OOP, many patterns take on a unique implementation. In this article, we will break down three groups of patterns: creational, structural, and behavioral — with practical examples in Golang.



The material will be useful for both beginners and experienced Go developers striving to write clean, reusable, and testable code.



1. Creational Patterns



Creational patterns manage the process of object creation. In Go, where there are no constructors or inheritance, their implementation is particularly elegant.



1.1. Singleton



Ensures that a type has only one instance and provides a global point of access to it. In Go, it is often implemented using sync.Once.



package singleton

import ( "sync")

type Database struct { connection string}

var ( instance *Database once sync.Once)

func GetDatabase() *Database { once.Do(func() { instance = &Database{connection: "postgres://user:pass@localhost/db"} }) return instance}


When to use: working with connection pools, cache, logger.



1.2. Factory Method



Defines an interface for creating an object, but lets subclasses alter the type of object that will be created. In Go, the factory method is simply a function that returns an interface.



package factory

import "fmt"

// Product interfacetype INotification interface { Send(message string) string}

// Concrete productstype Email struct{}

func (e *Email) Send(msg string) string { return fmt.Sprintf("Email sent: %s", msg)}

type SMS struct{}

func (s *SMS) Send(msg string) string { return fmt.Sprintf("SMS sent: %s", msg)}

// Factory functionfunc CreateNotification(notifType string) INotification { switch notifType { case "email": return &Email{} case "sms": return &SMS{} default: return nil }}


When to use: when the type of object to be created is not known in advance.



1.3. Builder



Separates the construction of a complex object from its representation. In Go, the builder is a struct with setter methods that return the builder itself for chaining.



package builder

type Pizza struct { Size string Cheese bool Pepperoni bool}

type PizzaBuilder struct { pizza Pizza}

func NewPizzaBuilder() *PizzaBuilder { return &PizzaBuilder{}}

func (b *PizzaBuilder) SetSize(size string) *PizzaBuilder { b.pizza.Size = size return b}

func (b *PizzaBuilder) AddCheese() *PizzaBuilder { b.pizza.Cheese = true return b}

func (b *PizzaBuilder) AddPepperoni() *PizzaBuilder { b.pizza.Pepperoni = true return b}

func (b *PizzaBuilder) Build() Pizza { return b.pizza}


When to use: when an object has many optional parameters.



2. Structural Patterns



Structural patterns define ways to assemble objects and classes into larger structures, while keeping them flexible and efficient.



2.1. Adapter



Allows objects with incompatible interfaces to work together. In Go, interfaces and wrapper structs are used for this purpose.



package adapter

import "fmt"

// Target interfacetype USB interface { ConnectWithUSB() string}

// Adaptee (incompatible class)type MemoryCard struct{}

func (m *MemoryCard) ReadFromCard() string { return "Data read from memory card"}

// Adaptertype CardReader struct { card *MemoryC

Blogs

Book Recommendations