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 that pair or contrast
Half of knowing patterns is telling the look-alikes apart and spotting the natural pairings:
Same shape, different intent (the ones interviews love):
- Proxy vs Decorator vs Adapter — same/same/different interface; control access / add behavior / change interface.
- State vs Strategy — drives its own transitions / chosen from outside.
- Mediator vs Facade vs Observer — many-to-many hub / one-way subsystem door / one-to-many broadcast.
- Factory Method vs Abstract Factory — one product / a matched family.
- Template Method vs Strategy — fixed skeleton with holes / the whole algorithm swapped.
- Composite vs Decorator — a tree of many children / one wrapped component.
Patterns that combine:
- Command + Memento → undo/redo.
- Composite + Iterator (or Visitor) → walk and operate on a tree.
- Abstract Factory is built from Factory Methods, often returning a Singleton.
- Pipeline + Fan-out/Fan-in + Worker Pool/Semaphore → a bounded concurrent data flow.
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.
Related patterns
What design patterns are, why they exist, and why they look so different in Go than in Java or C++.
stdlibPatterns 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 / 51. 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.
5. State and Strategy have the same shape (delegate behind an interface). What tells them apart?
Structurally identical, different intent. A state machine moves itself between states (states often know each other); a strategy is set by the client and just runs. This 'same shape, different intent' theme recurs: Proxy vs Decorator vs Adapter, Mediator vs Facade.
Comments
Sign in with GitHub to join the discussion.