The whole catalog at a glance. Each row links to the full page; skim to find the right tool, then dive in.
🎯 How to read this
Intent is the one-line purpose. Idiomatic Go is the form you’ll actually write — often lighter than the textbook version, because Go’s interfaces, functions, embedding, and goroutines do a lot of the work.
Creational — how objects get made
| Pattern | Intent | Idiomatic Go |
|---|---|---|
| Singleton | One instance, one access point | sync.Once; a package-level var |
| Factory Method | Decide which concrete type to build, behind an interface | NewX() Iface; a creation method per context |
| Abstract Factory | Create a family of related objects that must match | A factory interface with several Create… methods |
| Builder | Construct a complex object step by step | Functional options: New(req, ...Option) |
| Prototype | Create new objects by cloning a configured one | Explicit Clone(); slices.Clone / maps.Clone |
Structural — how objects compose
| Pattern | Intent | Idiomatic Go |
|---|---|---|
| Adapter | Make an incompatible type fit an expected interface | A wrapper with the target’s methods (http.HandlerFunc) |
| Bridge | Separate an abstraction from its implementation | Hold the implementation as an interface field |
| Composite | Treat leaves and trees uniformly | An interface + a node holding []Iface |
| Decorator | Add behavior by wrapping, same interface | Embed the interface; io wrappers, middleware |
| Facade | One simple entry over a complex subsystem | A struct holding subsystems, coarse methods |
| Flyweight | Share immutable state across many objects | A cache/interning factory of shared values |
| Proxy | A stand-in that controls access | Same interface, wraps the subject (lazy, cache, ReverseProxy) |
Behavioral — how objects collaborate
| Pattern | Intent | Idiomatic Go |
|---|---|---|
| Chain of Responsibility | Pass a request along handlers until one handles it | HTTP middleware func(next) Handler |
| Command | Capture a request as an object (queue, log, undo) | Execute/Undo interface; or a func() |
| Interpreter | Evaluate sentences of a small grammar | Tree of Eval() nodes (Composite + recursion) |
| Iterator | Traverse a collection without exposing it | range, channels, or iter.Seq (Go 1.23) |
| Mediator | Centralize many-to-many communication | A hub struct colleagues talk to |
| Memento | Snapshot/restore state without breaking encapsulation | Opaque struct with unexported fields |
| Observer | Notify many dependents on change | An interface list, or channels + goroutines |
| State | Change behavior with internal state | One type per state implementing a State interface |
| Strategy | Swap interchangeable algorithms at runtime | A func value or small interface (sort.Slice) |
| Template Method | Fixed skeleton, caller fills the steps | Inject steps as an interface or func fields |
| Visitor | Add operations to a stable set of types | Accept/Visit double dispatch, or a type switch |
Concurrency — the Go specials
| Pattern | Intent | Idiomatic Go |
|---|---|---|
| Generator | Lazy/infinite stream from a goroutine | func(done) <-chan T; producer closes the channel |
| Pipeline | Stream through channel-connected stages | Each stage owns and closes its output channel |
| Fan-out / Fan-in | Parallelize a stage, then merge results | N workers on one chan; WaitGroup to merge & close |
| Worker Pool | Bound concurrency with N workers | A jobs channel + a fixed set of workers |
| Semaphore | Cap how many run at once | Buffered chan struct{}; x/sync/semaphore |
| Context & Cancellation | Propagate cancel, deadline, request values | ctx first arg; select on ctx.Done() |
| Or-done | Range a stream that stops cleanly on cancel | Wrap each receive in a select on done |
| errgroup | Concurrent tasks: wait + first error + cancel | errgroup.WithContext, g.Go, g.SetLimit |
| Pub/Sub | Broadcast to dynamic subscribers | A broker: mutex-guarded map of channels |
Decision shortcuts — “I need to…”
✅ Pick a pattern by what you're trying to do
- …guarantee one instance → Singleton
- …hide which concrete type I build → Factory Method / Abstract Factory
- …handle many optional config fields → Builder (functional options)
- …add behavior to an object → Decorator
- …control access to an object → Proxy
- …make X fit interface Y → Adapter
- …put one door over a subsystem → Facade
- …swap an algorithm at runtime → Strategy (often just a func)
- …support undo/redo → Command + Memento
- …react when something changes → Observer / Pub/Sub
- …walk a tree → Composite + Iterator / Visitor
- …bound concurrency → Worker Pool / Semaphore
- …cancel a tree of goroutines → Context
- …run concurrent tasks that can fail → errgroup
Patterns Go quietly reshapes
🐹 Where a 'pattern' becomes a one-liner
- Strategy → a
funcvalue (sort.Slice’sless) - Iterator →
rangeanditer.Seq - Singleton →
sync.Once - Observer / Pub-Sub → channels + goroutines
- Decorator / Chain of Responsibility →
iowrappers and HTTP middleware - Template Method / Bridge → struct embedding + an interface field
- Factory → a plain
NewX()function
The lesson from Foundations: in Go, many patterns aren’t ceremonies you build — they’re the natural shape the language already nudges you toward.
Test yourself
Related patterns
What design patterns are, why they exist, and why they look so different in Go than in Java or C++.
STDLIB Patterns in the Go Standard LibraryA tour of the classic design patterns hiding in plain sight across Go's standard library.
Check your understanding
Score: 0 / 41. Wrapping an http.Handler to add logging, then auth, then gzip — each keeping the same interface — is which pattern?
Same interface in, same interface out, behavior added by wrapping — Decorator. In HTTP form it's a middleware chain (Chain of Responsibility too).
2. You launch a goroutine per request but want at most 5 hitting the API at once. Which pattern?
A counting semaphore caps how many goroutines are in the critical section while you still spawn one per task. A worker pool is the alternative when you have a fixed queue.
3. Letting callers decide how a slice is sorted by passing a comparison function is…
An interchangeable algorithm chosen at runtime — Strategy. In Go it's just a func value, exactly like sort.Slice's `less`.
4. You need exactly one shared config, initialized once, safe under concurrency.
One instance, one access point, initialized once — Singleton, and sync.Once makes the lazy version race-free.