Unlock High‑Maintainability Go Projects with 20+ Creational & Structural Design Patterns

This guide walks Go developers through essential creational and structural design patterns—Singleton, Simple Factory, Factory Method, Abstract Factory, Builder, and Facade—explaining their principles, Go implementations, pros and cons, and real‑world use cases to build scalable, loosely‑coupled systems.

Code Wrench
Code Wrench
Code Wrench
Unlock High‑Maintainability Go Projects with 20+ Creational & Structural Design Patterns

1️⃣ Singleton Pattern — Global Unique Object Management

Definition and Principle

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

Core idea: lazy initialization + thread‑safety + global access.

Go implementation (thread‑safe)

package singleton

import "sync"

type Config struct {
    Port int
    Env  string
}

var instance *Config
var once sync.Once

func GetConfig() *Config {
    once.Do(func() {
        instance = &Config{Port: 8080, Env: "dev"}
    })
    return instance
}

Pros and Cons

Pros: global uniqueness, resource saving, consistency.

Cons: hard to test, poor scalability.

Typical Applications

Web service configuration management

Database connection pool

Log manager

In high‑concurrency Go projects, use sync.Once to guarantee thread‑safe singleton creation and avoid duplicate objects across goroutines.

2️⃣ Simple Factory Pattern — Dynamic Object Creation

Definition and Principle

Creates objects based on parameters, centralizing creation logic.

Core idea: client does not new directly; it obtains objects via a factory method.

Go implementation

package factory

import "fmt"

type Payment interface { Pay(amount float64) }

type Alipay struct{}
func (a *Alipay) Pay(amount float64) { fmt.Println("支付宝支付:", amount) }

type WechatPay struct{}
func (w *WechatPay) Pay(amount float64) { fmt.Println("微信支付:", amount) }

func NewPayment(method string) Payment {
    switch method {
    case "alipay":
        return &Alipay{}
    case "wechat":
        return &WechatPay{}
    default:
        return nil
    }
}

Pros and Cons

Pros: decouples client from concrete implementations, easy to extend.

Cons: factory class can become bloated, may violate the Open/Closed Principle.

Application Example

E‑commerce payment system where the user selects a payment method and the factory creates the corresponding object.

Adding a new payment method only requires adding a new case in the factory, without changing client code.

3️⃣ Factory Method Pattern — Interface‑Based Object Creation

Definition and Principle

Defers object creation to subclasses; client depends on interfaces rather than concrete types.

Core idea: program to an interface and extend via inheritance.

Go implementation

package factorymethod

import "fmt"

type Payment interface { Pay(amount float64) }

type Alipay struct{}
func (a *Alipay) Pay(amount float64) { fmt.Println("支付宝支付:", amount) }

type PaymentFactory interface { CreatePayment() Payment }

type AlipayFactory struct{}
func (f *AlipayFactory) CreatePayment() Payment { return &Alipay{} }

func Pay(factory PaymentFactory, amount float64) {
    payment := factory.CreatePayment()
    payment.Pay(amount)
}

Pros and Cons

Pros: adheres to Open/Closed Principle; new types can be added without modifying client code.

Cons: increases number of classes, adds structural complexity.

Application Example

E‑commerce system supporting multiple payment methods (Alipay, WeChat, UnionPay) by adding new factories.

Each new payment method only needs a new concrete factory; client logic remains unchanged.

4️⃣ Abstract Factory Pattern — Family of Related Products

Definition and Principle

Provides an interface for creating families of related objects without specifying concrete classes.

Core idea: product families + unified factory interface.

Go implementation

package abstractfactory

import "fmt"

// Product interfaces
type Button interface { Render() }
type Checkbox interface { Render() }

// Windows products
type WinButton struct{}
func (b *WinButton) Render() { fmt.Println("渲染 Windows 按钮") }

type WinCheckbox struct{}
func (c *WinCheckbox) Render() { fmt.Println("渲染 Windows 复选框") }

// Mac products
type MacButton struct{}
func (b *MacButton) Render() { fmt.Println("渲染 Mac 按钮") }

type MacCheckbox struct{}
func (c *MacCheckbox) Render() { fmt.Println("渲染 Mac 复选框") }

// Factory interface
type GUIFactory interface {
    CreateButton() Button
    CreateCheckbox() Checkbox
}

// Windows factory
type WinFactory struct{}
func (f *WinFactory) CreateButton() Button { return &WinButton{} }
func (f *WinFactory) CreateCheckbox() Checkbox { return &WinCheckbox{} }

// Mac factory
type MacFactory struct{}
func (f *MacFactory) CreateButton() Button { return &MacButton{} }
func (f *MacFactory) CreateCheckbox() Checkbox { return &MacCheckbox{} }

Pros and Cons

Pros: ensures consistency across product families, simplifies system extension.

Cons: extending product families can be difficult; adding new products may require interface changes.

Application Example

Cross‑platform UI frameworks supporting Windows, macOS, Linux.

Clients can use the abstract factory without caring about the underlying platform, achieving once‑write‑multiple‑platform execution.

5️⃣ Builder Pattern — Construct Complex Objects Step‑by‑Step

Definition and Principle

Separates construction of a complex object from its representation, allowing the same construction process to create different representations.

Core idea: stepwise construction + fluent chaining + optional components.

Go implementation

type House struct {
    Floor string
    Wall  string
    Roof  string
}

type HouseBuilder struct { house House }

func (b *HouseBuilder) SetFloor(floor string) *HouseBuilder { b.house.Floor = floor; return b }
func (b *HouseBuilder) SetWall(wall string) *HouseBuilder { b.house.Wall = wall; return b }
func (b *HouseBuilder) SetRoof(roof string) *HouseBuilder { b.house.Roof = roof; return b }
func (b *HouseBuilder) Build() House { return b.house }

Application Example

Game map generation, complex UI pages, HTTP request builders.

Fluent chaining improves readability and maintainability while allowing flexible selection of construction steps.

6️⃣ Facade Pattern — Simplify Subsystem Interfaces

Definition and Principle

Provides a unified interface to a set of interfaces in a subsystem, making the subsystem easier to use.

Core idea: encapsulate complex logic behind a single entry point.

Go implementation

type SubsystemA struct{}
func (s *SubsystemA) OperationA() { fmt.Println("子系统A操作") }

type SubsystemB struct{}
func (s *SubsystemB) OperationB() { fmt.Println("子系统B操作") }

type Facade struct { A *SubsystemA; B *SubsystemB }
func (f *Facade) Operation() { f.A.OperationA(); f.B.OperationB() }

Application Example

Microservice gateway aggregating multiple service APIs into a single unified API for clients.

Clients interact with the Facade without needing to know the details of each subsystem.

Summary

Creational patterns address object‑creation complexity, ensuring extensibility and decoupling.

Structural patterns help combine system modules and simplify interfaces.

Go’s features—interfaces, structs, sync.Once, and fluent chaining—are leveraged effectively in these pattern implementations.

Next article will cover behavioral patterns, teaching how to make Go objects “smarter”, reduce if‑else statements, and further improve maintainability.
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Design PatternsBackend DevelopmentGoSingletonFactoryBuilderFacade
Code Wrench
Written by

Code Wrench

Focuses on code debugging, performance optimization, and real-world engineering, sharing efficient development tips and pitfall guides. We break down technical challenges in a down-to-earth style, helping you craft handy tools so every line of code becomes a problem‑solving weapon. 🔧💻

0 followers
Reader feedback

How this landed with the community

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.