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.
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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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. 🔧💻
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
