Design Patterns & LLD — Go-To Mind Map Notes
Source: Dive Into Design Patterns (Alexander Shvets / Refactoring.Guru, v2023)
Format: recall-first mind map — every node is Q → hook → when → tiny Go skeleton
Diagrams: Mermaid (mindmap/flowchart)
Language: Go (interfaces + composition; no classical inheritance trees)
How to use these notes (recall pattern)
flowchart TD
C["Central problem: What varies?"]
C --> CREATE["CREATE objects"]
C --> STRUCTURE["STRUCTURE objects"]
C --> BEHAVE["COMMUNICATE / assign duties"]
Active recall loop (best retention):
flowchart LR
Q[Trigger Q] --> Say[Say one-liner]
Say --> Sketch[Sketch participants]
Sketch --> Code[Write Go skeleton]
Code --> Check[Check notes]
Check --> Drill[Confusion pairs]
- Cover the answer. Read only the Trigger Q.
- Say the one-liner out loud (Feynman).
- Sketch the participants from memory.
- Write 5–10 lines of Go from memory, then check.
- Drill confusion pairs (Decorator vs Proxy, Strategy vs State, etc.).
Atomic card shape (every pattern):
| Field | Purpose |
|-------|---------|
| Trigger Q | When does this fire in an LLD/interview? |
| One-liner | Intent in one breath |
| Hook | Real-world metaphor to lock memory |
| Structure | Participants (Go names) |
| Skeleton | Minimal idiomatic Go |
| Trap | Common mix-up / anti-pattern |
ROOT MAP
mindmap
root((Design Patterns & LLD))
0 Mindset
1 OOP Foundations
2 Relations UML ladder
3 Design Principles
4 SOLID
5 Creational
how objects are born
6 Structural
how objects are wired
7 Behavioral
how objects talk / change
8 Decision tree
9 LLD interview playbook
0. Mindset
Trigger Q: Pattern vs algorithm?
Answer: Algorithm = recipe (exact steps). Pattern = blueprint (shape of solution; code differs per app).
Why learn: toolkit of proven designs + shared vocabulary ("use a Strategy here").
Levels of reuse (Gamma):
flowchart LR
Classes["Classes / libraries"] --> Patterns["Design patterns"]
Patterns --> Frameworks["Frameworks"]
Good design aims for: code reuse + extensibility (change is constant) without rigid coupling.
1. OOP Foundations
1.1 Objects & classes
Hook: Class = blueprint; object = instance (Oscar the Cat).
State = fields; behavior = methods; members = both.
1.2 Four pillars
| Pillar | One-liner | Go feel |
|---|---|---|
| Abstraction | Model only what the context needs (flight sim Airplane ≠ booking Airplane) | types that omit irrelevant detail |
| Encapsulation | Hide internals; expose a small public surface | unexported fields + methods |
| Inheritance | Build new types on existing ones | embedding (limited; prefer interfaces) |
| Polymorphism | Same call, different runtime behavior | interface satisfaction |
// Polymorphism via interface — Go's native "program to an interface"
type Flyer interface {
Fly(origin, dest string, passengers int) error
}
type Airport struct{}
func (a Airport) Depart(f Flyer) error {
return f.Fly("DEL", "BLR", 180) // works for Airplane, Helicopter, …
}
1.3 Relations between objects (strength ladder)
flowchart LR
Dep[Dependency] --> Assoc[Association]
Assoc --> Agg[Aggregation]
Agg --> Comp[Composition]
Dep -.->|"weak → strong"| Comp
flowchart TB
subgraph Type relations
Inh[Inheritance — is-a]
Impl[Implementation — can / fulfills contract]
end
| Relation | Meaning | Memory |
|---|---|---|
| Dependency | Uses briefly (param/local); change may break you | "borrows" |
| Association | Long-lived link (field / always reachable) | "knows" |
| Aggregation | Whole–part; parts can outlive whole | "has (shared)" |
| Composition | Whole owns part lifecycle | "owns" |
| Inheritance | Is-a; reuses interface + impl | "is" |
| Implementation | Fulfills a contract | "can" |
2. Design Principles (pre-SOLID)
Encapsulate What Varies
Q: Where will change hurt most?
Do: Pull varying logic into its own method/type so the stable core doesn't thrash.
Hook: Ship compartments — a mine floods one bay, not the hull.
func (o Order) Total() float64 {
sum := 0.0
for _, li := range o.Items {
sum += li.Price * float64(li.Qty)
}
return sum * (1 + TaxRate(o.Country)) // variation isolated
}
func TaxRate(country string) float64 {
switch country {
case "US":
return 0.07
case "EU":
return 0.20
default:
return 0
}
}
Program to an Interface, not an Implementation
Q: Can I swap the collaborator without editing callers?
Do: Depend on the methods you need, not the concrete type.
type Employee interface {
DoWork()
}
type Company struct{}
func (c Company) RunDay(staff []Employee) {
for _, e := range staff {
e.DoWork() // not *Designer, *Dev — the interface
}
}
Favor Composition Over Inheritance
Q: Am I multiplying subclasses across dimensions (engine × cargo × nav)?
Do: "Has-a" + delegate. Runtime-swappable behaviors.
Trap: Inheritance = one dimension; multi-dimension → combinatorial explosion.
type Engine interface{ Torque() float64 }
type Navigator interface{ Route(to string) []string }
type Vehicle struct {
Engine
Navigator
}
func (v Vehicle) Drive(to string) {
_ = v.Torque()
_ = v.Route(to)
}
3. SOLID
| Letter | Rule | Recall phrase |
|---|---|---|
| S | One reason to change | "one job, one class" |
| O | Open for extension, closed for modification | "add types, don't edit old ones" |
| L | Subtypes must be substitutable | "don't surprise the caller" |
| I | No fat interfaces | "don't force unused methods" |
| D | High-level depends on abstractions | "details plug into policy" |
SRP
// BAD: Employee manages data AND prints timesheets
// GOOD:
type Employee struct{ ID, Name string }
type TimesheetReporter struct{}
func (TimesheetReporter) Print(e Employee) { /* format may change alone */ }
OCP (+ Strategy)
type Shipping interface{ Cost(order Order) float64 }
type Order struct{ Shipping Shipping }
func (o Order) ShippingCost() float64 { return o.Shipping.Cost(o) }
// new shipping = new type; Order untouched
LSP checklist (Go interfaces)
- Don't strengthen preconditions / weaken postconditions in implementations
- Don't throw unexpected errors the contract didn't advertise
- No "is this the concrete type?" branches that break substitution
ISP
// BAD: CloudProvider with 40 methods
// GOOD: split
type BlobStore interface{ Put(key string, b []byte) error }
type Queue interface{ Publish(topic string, msg []byte) error }
DIP
type ReportStore interface {
Load(id string) (Report, error)
Save(Report) error
}
type BudgetReport struct{ Store ReportStore } // high-level depends on interface
// PostgresStore / FileStore implement ReportStore — details depend on abstraction
5. CREATIONAL — "how are objects born?"
mindmap
root((Creational))
Factory Method
subclass decides product
Abstract Factory
families of products
Builder
step-by-step complex object
Prototype
clone existing instance
Singleton
one shared instance
Factory Method
AKA: Virtual Constructor
Trigger Q: I know the interface of a product, but not which concrete type until a subclass/config decides?
One-liner: Creator defines CreateProduct(); subclasses return concrete products.
Hook: Logistics app — Transport is Truck or Ship; Logistics.CreateTransport() deferred to RoadLogistics / SeaLogistics.
When: framework/library hooks; parallel product hierarchies; replace new Concrete sprinkled everywhere.
type Button interface{ Render() }
type Dialog interface {
CreateButton() Button // factory method
Render()
}
type WindowsButton struct{}
func (WindowsButton) Render() { /* native win btn */ }
type WindowsDialog struct{}
func (WindowsDialog) CreateButton() Button { return WindowsButton{} }
func (d WindowsDialog) Render() {
btn := d.CreateButton()
btn.Render()
}
Trap: Not the same as Abstract Factory (one product vs families).
Relates: often grows into Abstract Factory; pairs with Template Method; Iterator may use it for iterators.
Abstract Factory
Trigger Q: Need families of related products (WinButton+WinCheckbox) and must keep them consistent?
One-liner: Interface of factory methods for each product in a family; concrete factories produce one family.
Hook: Cross-platform UI kit — GUIFactory → WinFactory / MacFactory.
type Button interface{ Paint() }
type Checkbox interface{ Paint() }
type GUIFactory interface {
CreateButton() Button
CreateCheckbox() Checkbox
}
type WinFactory struct{}
func (WinFactory) CreateButton() Button { return WinButton{} }
func (WinFactory) CreateCheckbox() Checkbox { return WinCheckbox{} }
func Application(f GUIFactory) {
f.CreateButton().Paint()
f.CreateCheckbox().Paint()
}
Trap: Adding a new product type forces changing the factory interface (and all factories).
Builder
Trigger Q: Constructor has 10+ params / many optional steps / same process, different representations?
One-liner: Build step-by-step; director can reuse the recipe; get product at the end.
Hook: House builder — walls, doors, roof; same steps → wooden or stone house.
type House struct{ Walls, Doors, Roof string }
type HouseBuilder interface {
BuildWalls()
BuildDoors()
BuildRoof()
GetHouse() House
}
type WoodBuilder struct{ h House }
func (b *WoodBuilder) BuildWalls() { b.h.Walls = "wood" }
func (b *WoodBuilder) BuildDoors() { b.h.Doors = "wood" }
func (b *WoodBuilder) BuildRoof() { b.h.Roof = "wood" }
func (b *WoodBuilder) GetHouse() House { return b.h }
type Director struct{}
func (Director) Construct(b HouseBuilder) House {
b.BuildWalls(); b.BuildDoors(); b.BuildRoof()
return b.GetHouse()
}
Go tip: fluent setters (WithX() *Builder) are an idiomatic Builder variant.
Trap: Overkill for simple structs — use functional options for light cases.
Prototype
Trigger Q: Creating from scratch is expensive / I don't want to depend on concrete classes to copy?
One-liner: Clone existing objects via a common Clone() contract.
Hook: Cell mitosis; shape editor duplicate.
type Shape interface {
Clone() Shape
Draw()
}
type Circle struct {
X, Y, R int
Color string
}
func (c Circle) Clone() Shape {
cp := c // shallow copy; deep-copy slices/maps if needed
return &cp
}
func (c Circle) Draw() { /* … */ }
Trap: Deep vs shallow copy bugs with nested references.
Singleton
Trigger Q: Exactly one instance + global access (config, logger, connection pool)?
One-liner: Ensure one instance; provide a single access point.
Hook: Government / one president.
package config
import "sync"
type Config struct{ DSN string }
var (
once sync.Once
inst *Config
)
func Get() *Config {
once.Do(func() { inst = &Config{DSN: "postgres://…"} })
return inst
}
Trap: Hidden global state; hard to test — prefer DI; if needed, use sync.Once (thread-safe).
Pros: controlled access, lazy init. Cons: violates SRP often; masks dependencies.
6. STRUCTURAL — "how are objects wired?"
mindmap
root((Structural))
Adapter
make incompatible APIs work
Bridge
split abstraction from implementation
Composite
tree of part / whole
Decorator
wrap to add behavior
Facade
simplify a subsystem
Flyweight
share intrinsic state RAM
Proxy
stand-in controlling access
Adapter
AKA: Wrapper
Trigger Q: Third-party / legacy API shape ≠ what my code expects?
One-liner: Translate one interface into another.
Hook: Power plug adapter.
type JSONAnalytics interface {
AnalyzeJSON(data []byte) (string, error)
}
// legacy
type XMLService struct{}
func (XMLService) AnalyzeXML(xml string) string { return "ok" }
type XMLToJSONAdapter struct{ Inner XMLService }
func (a XMLToJSONAdapter) AnalyzeJSON(data []byte) (string, error) {
xml := jsonToXML(data) // conversion
return a.Inner.AnalyzeXML(xml), nil
}
Confusion: Adapter changes interface; Decorator keeps interface & adds behavior; Facade simplifies a subsystem.
Bridge
Trigger Q: Two independent dimensions both need to vary (shape × renderer; remote × device)?
One-liner: Split into Abstraction + Implementation hierarchies linked by composition.
Hook: Remote control (abstraction) ↔ Device (TV/Radio implementation).
type Device interface {
IsOn() bool
On(); Off()
SetVolume(int)
}
type Remote struct{ Dev Device }
func (r *Remote) Toggle() {
if r.Dev.IsOn() {
r.Dev.Off()
} else {
r.Dev.On()
}
}
type AdvancedRemote struct{ Remote }
func (r *AdvancedRemote) Mute() { r.Dev.SetVolume(0) }
Trap: Looks like Strategy; Bridge is about structural decoupling of two hierarchies long-term.
Composite
Trigger Q: Tree of objects; clients should treat leaf and group the same?
One-liner: Uniform component interface for leaves and composites.
Hook: File system / org chart / nested boxes in a graphics editor.
type Graphic interface {
Draw()
Move(dx, dy int)
}
type Dot struct{ X, Y int }
func (d *Dot) Draw() { /* point */ }
func (d *Dot) Move(dx, dy int) { d.X += dx; d.Y += dy }
type Compound struct{ Children []Graphic }
func (c *Compound) Draw() {
for _, ch := range c.Children {
ch.Draw()
}
}
func (c *Compound) Move(dx, dy int) {
for _, ch := range c.Children {
ch.Move(dx, dy)
}
}
Decorator
AKA: Wrapper
Trigger Q: Add responsibilities at runtime without exploding subclasses?
One-liner: Stack wrappers that share the component interface.
Hook: Wearing clothes — layers.
type Notifier interface{ Send(msg string) }
type EmailNotifier struct{}
func (EmailNotifier) Send(msg string) { /* email */ }
type SMSDecorator struct{ Inner Notifier }
func (d SMSDecorator) Send(msg string) {
d.Inner.Send(msg)
/* also SMS */
}
type SlackDecorator struct{ Inner Notifier }
func (d SlackDecorator) Send(msg string) {
d.Inner.Send(msg)
/* also Slack */
}
// usage: SlackDecorator{SMSDecorator{EmailNotifier{}}}
Confusion pair: Decorator = add behavior, same interface. Proxy = control access (lazy, auth, remote). Adapter = change interface.
Facade
Trigger Q: Client talks to a messy subsystem of many classes?
One-liner: One simple entry API over a complex subsystem.
Hook: Ordering pizza by phone — one number, kitchen chaos hidden.
type VideoConverter struct {
// holds ffmpeg, codec, bitrate helpers…
}
func (VideoConverter) Convert(filename, format string) string {
// orchestrate: decode → filter → encode → write
return "output." + format
}
Trap: Don't let Facade become a god object — keep it a thin orchestrator.
Flyweight
Trigger Q: Millions of similar objects; RAM blown by duplicated immutable data?
One-liner: Share intrinsic (immutable) state; pass extrinsic state in methods.
Hook: Forest of trees — shared TreeType (texture/color), many Tree positions.
type TreeType struct{ Name, Color, Texture string } // intrinsic, shared
type TreeTypeFactory struct{ cache map[string]*TreeType }
func (f *TreeTypeFactory) Get(name, color, texture string) *TreeType {
key := name + color + texture
if t, ok := f.cache[key]; ok {
return t
}
t := &TreeType{name, color, texture}
f.cache[key] = t
return t
}
type Tree struct {
X, Y int
Type *TreeType // flyweight
}
func (t Tree) Draw() { /* use t.X,t.Y + shared Type */ }
Proxy
Trigger Q: Need lazy load, access control, logging, caching, or remote stub in front of a real object?
One-liner: Same interface as the real subject; proxy delegates after extra work.
Hook: Credit card as proxy for bank account.
type Image interface{ Display() }
type RealImage struct{ Path string }
func (r *RealImage) Display() { /* heavy load from disk */ }
type ImageProxy struct {
Path string
real *RealImage
}
func (p *ImageProxy) Display() {
if p.real == nil {
p.real = &RealImage{Path: p.Path} // lazy
}
// optional: auth / cache / log
p.real.Display()
}
Types: virtual (lazy), protection (ACL), remote, logging/smart reference.
7. BEHAVIORAL — "how do objects talk / change?"
mindmap
root((Behavioral))
Chain of Responsibility
pass request along handlers
Command
request as object
Iterator
traverse without exposing structure
Mediator
hub for colleague communication
Memento
snapshot / undo
Observer
pub-sub
State
behavior by internal state
Strategy
swap algorithms
Template Method
algorithm skeleton + hooks
Visitor
externalize operations on a structure
Chain of Responsibility
AKA: CoR, Chain of Command
Trigger Q: Multiple handlers might process a request; avoid hard-coded if/else towers?
One-liner: Handlers linked in a chain; each handles or forwards.
Hook: Support tiers L1→L2→L3; corporate purchase approvals.
type Handler interface {
SetNext(Handler) Handler
Handle(amount float64) string
}
type base struct{ next Handler }
func (b *base) SetNext(h Handler) Handler { b.next = h; return h }
func (b *base) forward(amount float64) string {
if b.next != nil {
return b.next.Handle(amount)
}
return "unhandled"
}
type Manager struct{ base }
func (m *Manager) Handle(amount float64) string {
if amount <= 1000 {
return "manager approved"
}
return m.forward(amount)
}
type Director struct{ base }
func (d *Director) Handle(amount float64) string {
if amount <= 10000 {
return "director approved"
}
return d.forward(amount)
}
Relates: often with Composite; can use Command as the request object.
Command
Trigger Q: Need undo, queue, log, or schedule operations?
One-liner: Encapsulate a request as an object (receiver + args).
Hook: Restaurant order ticket; remote control buttons.
type Command interface {
Execute()
Undo()
}
type Editor struct{ Text string }
type CopyCommand struct{ Ed *Editor; clipboard *string }
func (c *CopyCommand) Execute() { *c.clipboard = c.Ed.Text }
func (c *CopyCommand) Undo() {}
type PasteCommand struct {
Ed *Editor
clipboard *string
backup string
}
func (c *PasteCommand) Execute() {
c.backup = c.Ed.Text
c.Ed.Text += *c.clipboard
}
func (c *PasteCommand) Undo() { c.Ed.Text = c.backup }
type Button struct{ Cmd Command }
func (b Button) Click() { b.Cmd.Execute() }
Iterator
Trigger Q: Walk a collection without exposing list/tree/graph guts?
One-liner: Iterator interface (HasNext/Next); collection creates it.
Hook: TV remote channel surfing; Go's iter / for range spirit.
type Iterator[T any] interface {
HasNext() bool
Next() T
}
type SliceIter[T any] struct {
data []T
i int
}
func (it *SliceIter[T]) HasNext() bool { return it.i < len(it.data) }
func (it *SliceIter[T]) Next() T {
v := it.data[it.i]
it.i++
return v
}
Go note: prefer for range / iter.Seq in modern Go; pattern still useful for custom graphs.
Mediator
AKA: Intermediary, Controller
Trigger Q: Many objects tangled in pairwise references (UI dialog chaos)?
One-liner: Colleagues talk only to a mediator; mediator coordinates.
Hook: Airport control tower; chat room.
type Mediator interface {
Notify(sender Colleague, event string)
}
type Colleague interface {
SetMediator(Mediator)
}
type AuthDialog struct { /* form fields */ }
func (d *AuthDialog) Notify(sender Colleague, event string) {
switch event {
case "loginClick":
// validate fields, call auth service…
case "checkboxChanged":
// enable/disable controls…
}
}
type Button struct {
med Mediator
}
func (b *Button) SetMediator(m Mediator) { b.med = m }
func (b *Button) Click() { b.med.Notify(b, "loginClick") }
Confusion: Mediator centralizes communication; Observer broadcasts events (looser).
Memento
AKA: Snapshot
Trigger Q: Undo/rollback without violating encapsulation of originator state?
One-liner: Originator creates opaque memento; caretaker stores history.
Hook: Text editor undo stack; game save checkpoint.
type Memento struct{ state string } // opaque to outsiders in stricter designs
type Editor struct{ text string }
func (e *Editor) Type(s string) { e.text += s }
func (e *Editor) Save() Memento { return Memento{e.text} }
func (e *Editor) Restore(m Memento) { e.text = m.state }
type History struct{ stack []Memento }
func (h *History) Push(m Memento) { h.stack = append(h.stack, m) }
func (h *History) Pop() (Memento, bool) {
if len(h.stack) == 0 {
return Memento{}, false
}
m := h.stack[len(h.stack)-1]
h.stack = h.stack[:len(h.stack)-1]
return m, true
}
Observer
AKA: Event-Subscriber, Listener
Trigger Q: Many objects must react when one object's state changes?
One-liner: Subject maintains subscribers; notifies on change.
Hook: YouTube subscriptions; newspaper subscriptions.
type Observer interface{ Update(temp float64) }
type Subject interface {
Attach(Observer)
Detach(Observer)
Notify()
}
type WeatherStation struct {
temp float64
obs []Observer
}
func (w *WeatherStation) Attach(o Observer) { w.obs = append(w.obs, o) }
func (w *WeatherStation) SetTemp(t float64) {
w.temp = t
w.Notify()
}
func (w *WeatherStation) Notify() {
for _, o := range w.obs {
o.Update(w.temp)
}
}
type PhoneDisplay struct{}
func (PhoneDisplay) Update(temp float64) { /* UI */ }
Go tip: channels / event buses are common idioms; same pub-sub idea.
State
Trigger Q: Object behavior changes drastically with internal state (and you have state×method switch hell)?
One-liner: Delegate behavior to state objects; transitions swap the state.
Hook: Vending machine; document Draft→Moderation→Published.
type State interface {
Publish(*Document)
}
type Document struct {
state State
}
func (d *Document) SetState(s State) { d.state = s }
func (d *Document) Publish() { d.state.Publish(d) }
type Draft struct{}
func (Draft) Publish(d *Document) { d.SetState(Moderation{}) }
type Moderation struct{}
func (Moderation) Publish(d *Document) { d.SetState(Published{}) }
type Published struct{}
func (Published) Publish(*Document) { /* already published */ }
Confusion pair: State ≈ Strategy structurally; State is aware of transitions / tied to context lifecycle; Strategy is usually injected & unaware of siblings.
Strategy
Trigger Q: Family of interchangeable algorithms selected at runtime?
One-liner: Context holds a Strategy interface; swap implementations.
Hook: Navigation — driving / walking / public transport routes; payment methods.
type RouteStrategy interface {
BuildRoute(a, b string) []string
}
type Navigator struct{ Strategy RouteStrategy }
func (n Navigator) Route(a, b string) []string { return n.Strategy.BuildRoute(a, b) }
type RoadStrategy struct{}
func (RoadStrategy) BuildRoute(a, b string) []string { return []string{a, "highway", b} }
type WalkStrategy struct{}
func (WalkStrategy) BuildRoute(a, b string) []string { return []string{a, "path", b} }
Relates: OCP's poster child; similar to Bridge but focused on algorithms, not long-lived dual hierarchies.
Template Method
Trigger Q: Same algorithm skeleton; only some steps differ by subtype?
One-liner: Abstract steps in a fixed-order method; override hooks.
Hook: Data miner — open → extract → parse → analyze → close.
type DataMiner interface {
Open()
Extract() []byte
Parse([]byte) any
Analyze(any)
Close()
}
func Mine(m DataMiner) { // template method as function (Go-friendly)
m.Open()
defer m.Close()
raw := m.Extract()
data := m.Parse(raw)
m.Analyze(data)
}
type PDFMiner struct{}
func (PDFMiner) Open() {}
func (PDFMiner) Extract() []byte { return nil }
func (PDFMiner) Parse([]byte) any { return nil }
func (PDFMiner) Analyze(any) {}
func (PDFMiner) Close() {}
Relates: Factory Method is often one step inside a Template Method.
Trap: Heavy inheritance; in Go prefer function template + interface hooks (as above).
Visitor
Trigger Q: Many unrelated operations on an object structure, and you can't keep stuffing methods into element classes?
One-liner: External visitor with VisitConcreteA/B; elements Accept(v).
Hook: Insurance company agent visiting buildings; export/XML/JSON ops on a document AST.
type Shape interface {
Accept(Visitor)
}
type Visitor interface {
VisitDot(*Dot)
VisitCircle(*Circle)
}
type Dot struct{ X, Y int }
func (d *Dot) Accept(v Visitor) { v.VisitDot(d) }
type Circle struct{ X, Y, R int }
func (c *Circle) Accept(v Visitor) { v.VisitCircle(c) }
type XMLExportVisitor struct{}
func (XMLExportVisitor) VisitDot(d *Dot) { /* export dot */ }
func (XMLExportVisitor) VisitCircle(c *Circle) { /* export circle */ }
Trap: Adding new element types is painful (must update Visitor). Best when element set is stable and operations grow.
8. Decision tree (interview speed)
flowchart TD
Start{What do you need?}
Start --> Create[CREATE]
Start --> Structure[STRUCTURE / wrap]
Start --> Behave[BEHAVIOR / communication]
Create --> C1[one of several products] --> FM[Factory Method]
Create --> C2[family of products] --> AF[Abstract Factory]
Create --> C3[complex step build] --> B[Builder]
Create --> C4[copy existing] --> P[Prototype]
Create --> C5[exactly one] --> S[Singleton — last resort]
Structure --> S1[incompatible API] --> Ad[Adapter]
Structure --> S2[two varying hierarchies] --> Br[Bridge]
Structure --> S3[tree part/whole] --> Co[Composite]
Structure --> S4[add behavior same API] --> De[Decorator]
Structure --> S5[simplify subsystem] --> Fa[Facade]
Structure --> S6[share RAM state] --> Fl[Flyweight]
Structure --> S7[control access / lazy / remote] --> Pr[Proxy]
Behave --> B1[pass along handlers] --> Ch[Chain of Responsibility]
Behave --> B2[undo / queue / log action] --> Cm[Command]
Behave --> B3[traverse collection] --> It[Iterator]
Behave --> B4[untangle many-to-many talk] --> Me[Mediator]
Behave --> B5[snapshot / undo state] --> Mm[Memento]
Behave --> B6[notify many listeners] --> Ob[Observer]
Behave --> B7[behavior by state machine] --> St[State]
Behave --> B8[swap algorithm] --> Sy[Strategy]
Behave --> B9[shared algorithm skeleton] --> Tm[Template Method]
Behave --> B10[add ops to stable structure] --> Vi[Visitor]
9. Confusion pairs (drill these)
| Pair | Difference |
|---|---|
| Adapter vs Decorator vs Proxy | change interface / add behavior / control access |
| Adapter vs Facade | one object vs whole subsystem simplification |
| Strategy vs State | injected algorithm vs self-transitioning states |
| Strategy vs Bridge | algorithm swap vs dual hierarchy decoupling |
| Mediator vs Observer | central coordinator vs distributed pub-sub |
| Chain vs Decorator | stop/forward request vs stack all behaviors |
| Factory Method vs Abstract Factory | one product hook vs product families |
| Composite vs Decorator | tree of children vs linear wrapper chain |
| Command vs Strategy | request object (often undoable) vs replaceable algorithm |
| Template Method vs Strategy | inheritance/hooks vs composition/delegate |
10. LLD practices checklist (from the book → Go habits)
- Find what varies → extract interface / strategy / state.
-
Depend on interfaces → accept
interface{…}at boundaries. - Compose → embed small collaborators; avoid deep type hierarchies.
- SRP → split God structs.
- OCP → add types, don't edit battle-tested cores.
- LSP → implementations must honor the contract.
-
ISP → small interfaces (
io.Readerstyle). - DIP → high-level packages define interfaces; infra implements.
- Name the pattern in design docs — shared language.
- Don't pattern-hunt — complexity only when change pressure justifies it.
11. Quick Go idiom map (book OOP → Go)
| Book concept | Go idiom |
|---|---|
| Abstract class | Interface + optional helper funcs |
| Protected members | Same package / unexported |
| Multiple inheritance | Interface embedding |
| Polymorphism | Implicit interface satisfaction |
| Singleton |
sync.Once or DI container |
| Observer | interfaces, channels, event bus |
| Iterator |
for range, iter.Seq
|
| Decorator/Proxy | wrapping structs implementing same interface |
Top comments (0)