CMD Guide
HomeOO & Low-Level DesignDesign Patterns Overview

What are Design Patterns

What are Design Patterns

1. Intuition — the problem they solve

When engineers design systems independently, they keep re-discovering the same recurring shapes. "I need one shared config object across the app." "I need to swap the payment provider without touching checkout code." "I need to notify many subscribers when one thing changes." Left unnamed, each engineer reinvents a slightly different solution, and code reviews devolve into re-explaining the same structural idea from scratch.

Design patterns exist to give these recurring solutions names and vetted structures. They are the distilled, battle-tested answers to problems that appear again and again in object-oriented design — extracted from real systems by practitioners (most famously the 1994 "Gang of Four" book) rather than invented top-down. The payoff is twofold: you don't reinvent, and you gain a shared vocabulary. Saying "let's put a Strategy here" transmits an entire design in two words to anyone who knows the catalog.

2. Precise definition / how it works

A design pattern is a named, reusable solution to a recurring design problem within a given context. Crucially, a pattern is not a library, a finished class, or copy-paste code. It is a template — a description of the participating roles (classes/objects), their relationships, and responsibilities — that you adapt to your concrete situation. The same pattern looks different in every codebase because you supply the domain types.

The GoF catalog groups 23 classic patterns into three families by what problem they address:

Almost every pattern is a specific application of two deeper principles: program to an interface, not an implementation, and favor composition over inheritance. If you internalize those two, most patterns feel like inevitable consequences rather than tricks to memorize.

3. Concrete example — Strategy

Consider a shipping-cost calculator that must support several algorithms (flat rate, weight-based, third-party API) and let us swap them at runtime. Hard-coding an if/else chain couples the caller to every algorithm and forces edits to a central method each time a new one appears. Strategy factors each algorithm behind a common interface so the context holds one interchangeable reference.

// The strategy interface — the stable abstraction
interface ShippingCost {
    BigDecimal cost(Order order);
}

// Interchangeable concrete strategies
class FlatRate implements ShippingCost {
    public BigDecimal cost(Order o) { return new BigDecimal("5.00"); }
}
class ByWeight implements ShippingCost {
    public BigDecimal cost(Order o) {
        return o.weightKg().multiply(new BigDecimal("1.20"));
    }
}

// The context depends on the interface, not any concrete class
class Checkout {
    private ShippingCost strategy;              // injected / swappable
    Checkout(ShippingCost strategy) { this.strategy = strategy; }
    void setStrategy(ShippingCost s) { this.strategy = s; }

    BigDecimal total(Order o) {
        return o.subtotal().add(strategy.cost(o)); // delegates, no branching
    }
}

// Usage: behavior chosen at runtime
Checkout c = new Checkout(new ByWeight());
c.setStrategy(new FlatRate());   // swap without touching Checkout's logic

When it applies: you have a family of interchangeable algorithms, want to pick one at runtime, or want to eliminate a growing conditional that selects behavior. Checkout never changes when you add a new ShippingCost — you just write a new class. That is the Open/Closed Principle made concrete.

4. When to use / when NOT — the judgment layer

Patterns are tools with a cost, not badges of quality. Reach for one only when the force it manages is actually present.

The meta-rule: patterns manage change and variation. If a dimension isn't going to vary, abstracting it is speculative complexity (YAGNI). Apply patterns reactively — when a real force appears — far more often than proactively.

5. Pitfalls / what interviewers probe

Key takeaways

🎯 Drill Ladder — survive the follow-ups

L0 · a design pattern is a named, adaptable template for a recurring OO problem — value comes only from the force it manages, not from its name.

L1 · ⑥ Cost/Simplicity — "make this extensible for future payment providers"
Trap: add a PaymentStrategy interface and a class per provider now, "so it's future-proof," even though exactly one provider exists today.
Bar: the deciding variable is N, the count of concrete variants that exist today plus a committed (not hoped-for) second one — at N=1 the interface, extra file, and extra test double are pure carrying cost with zero flexibility realized yet; ship the concrete class and extract the interface when N=2 actually lands (rule of three), because YAGNI abstraction is a liability, not an asset, until it's paying rent. connects-to: coupling vs. cohesion

L2 · ⑤ Adversary/Edge — "isn't this just Strategy?" asked of a state machine
Trap: "same shape, an interface plus swappable implementations — pick either, they're interchangeable."
Bar: the deciding variable is who owns the transition — State requires the object to switch its own internal reference as invariants change (self-driven, sequential), while Strategy is client-selected and static for the duration of one call; if you let a "Strategy" trigger the next strategy internally you've smuggled State's self-transition responsibility into a shape that was never designed to protect that invariant, so an adversarial "can it trigger the next one?" question exposes a broken encapsulation boundary, not a naming quibble. connects-to: State pattern

L3 · ① Concurrency — is your Singleton thread-safe?
Trap: "it's a Singleton, so by definition there's only one instance — that's what makes it safe."
Bar: the deciding variable is whether the lazy-init reference is volatile under double-checked locking — without it, the Java Memory Model permits the constructor's field writes to be reordered past the reference publish, so a second thread can see a non-null reference to a partially-constructed object; the fix is volatile DCL, the static holder idiom, or an enum singleton, and "only one instance" says nothing about visibility across threads. connects-to: Singleton thread-safe variants

L4 · ② Failure — a retry-Proxy wraps a caching-Decorator wraps the real service
Trap: "the layers are independent, just add a retry decorator on top and failures are handled."
Bar: the deciding variable is composition order, because it fixes which layer owns which failure domain — retry outside cache re-hits the real backend on a transient error, but retry inside cache can retry against (or poison) a cache that already absorbed a bad response; you must reason explicitly about which wrapped layer sees the failure first, not just stack more decorators and assume resilience composes for free. connects-to: Decorator pattern

L5 · ④ Time/Lifecycle — a two-year-old Visitor now needs a new type and a new operation
Trap: "Visitor is extensible — just add another visit method to the hierarchy."
Bar: the deciding variable is which axis of change the pattern optimized for at adoption time — Visitor trades cheap-new-operations for expensive-new-types (every visitor implementation must add a method), so once the change axis flips and types start outgrowing operations, the "correct" choice from two years ago has ossified into the costliest one to extend; the fix is re-deriving the pattern from the current change axis (often back to plain polymorphism), which is a migration, not a patch. connects-to: Visitor pattern

The floor keeps dropping: Singleton config + an Observer event bus + a Decorator middleware chain are composed in prod; a network partition splits the observer subscribers mid-broadcast while the Singleton's lazily-initialized state is being read by threads on both sides of the split. Diagnose the emergent failure, and given a two-hour deadline, name which pattern choice you'd undo first and why — "add more patterns" is never the answer under a clock.

Self-locate: died at L1 → mid-level; L4+ → staff signal.

Facing any new concept? Hit it with the six: concurrent? failing? at 100×? over time? adversarial? worth the cost? — that's the interviewer's whole playbook.

🤖 Don't fully get this? Learn it with Claude

Stuck on What are Design Patterns? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **What are Design Patterns** (OO & Low-Level Design) and want to truly understand it. Explain What are Design Patterns from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **What are Design Patterns** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **What are Design Patterns** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **What are Design Patterns** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes