Design Patterns Tutorial 0/70 lessons ~6 min read Lesson 21

    Prototype Pattern

    Prototype creates new objects by cloning an existing instance rather than calling a constructor.

    Course progress0%
    Focus
    21 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Prototype creates new objects by cloning an existing instance rather than calling a constructor. It earns its keep when construction is expensive (deep graphs, remote lookups, large default state) or when the concrete type is chosen at runtime. JavaScript's object model is prototype-based; in classical languages Prototype shows up in test fixtures, document templates, simulation contexts, and game entity spawning.

    The story

    A risk-analysis pipeline took 1.4s to construct SimulationContext — 17 reference tables loaded each run. Cloning a pre-built prototype cut setup to 9ms and made deterministic replay trivial — every clone started from the same canonical state.

    The business problem

    Repeated expensive construction dominates latency and CPU in high-volume paths:

    • Construction tax: 80%+ CPU rebuilding identical lookup tables per request.
    • Test verbosity: tests duplicate near-identical setup with tiny overrides.
    • Runtime type selection: registry picks concrete class — constructor not known at compile time.
    • Snapshot needs: undo, save-game, audit replay require cheap state copies.

    The problem teams faced

    Prototype fits when:

    • Construction is measurably expensive — profile before applying.
    • Many slight variants of a fully-formed object are needed.
    • Concrete type known only at runtime via registry.
    • Objects own shareable immutable state plus small mutable deltas.

    Understanding the topic

    Intent: Create objects by copying a prototypical instance.

    • Prototype — declares clone() method.
    • ConcretePrototype — implements clone(); deep or shallow copy strategy.
    • Registry — maps key → prototype; client clones on demand.
    • structuredClone (JS) — built-in deep clone for most values.

    Internal architecture

    Registry + clone pattern:

    ts
    interface Prototype { clone(): Prototype }
    class PrototypeRegistry {
    private prototypes = new Map<string, Prototype>()
    register(key: string, proto: Prototype) { this.prototypes.set(key, proto) }
    create(key: string): Prototype {
    const proto = this.prototypes.get(key)
    if (!proto) throw new Error(`Unknown: ${key}`)
    return proto.clone()
    }
    }

    Visual explanation

    Three diagrams cover clone flow, deep vs shallow, and registry:

    Prototype clone flow
    Build canonical once
    Startup / warmup
    Register prototype
    Key → instance
    Client clone()
    Cheap copy
    Override deltas
    Few fields only
    Pay construction once; clone cost per use.
    Deep vs shallow clone
    Shared immutable?
    Shallow OK
    Mutable nested?
    Deep clone
    Circular refs?
    Visited set
    structuredClone
    JS default
    Shared mutable state in clone causes spooky cross-instance bugs.
    Prototype vs Factory Method
    Construction cheap?
    Factory Method
    Construction expensive?
    Prototype
    Type at runtime?
    Registry + clone
    Resources (sockets)?
    Don't clone
    Don't clone objects owning non-clonable resources.

    Informative example

    Before / after — reconstruct vs clone:

    ts
    // ❌ Rebuild 17 tables every simulation run — 1.4s each
    function runSimulation(params: Params) {
    const ctx = new SimulationContext() // loads all reference tables
    ctx.apply(params)
    return engine.run(ctx)
    }
    // ✅ Clone pre-built prototype — 9ms setup
    const canonical = await SimulationContext.loadTables() // once at startup
    registry.register("default", canonical)
    function runSimulation(params: Params) {
    const ctx = registry.create("default").clone() as SimulationContext
    ctx.apply(params) // only deltas change
    return engine.run(ctx)
    }
    // JS: structuredClone for plain objects
    const copy = structuredClone(prototypeState)

    Execution workflow

    1Prototype lifecycle
    1 / 4

    Build canonical

    Expensive setup once at startup or warmup.

    Load tables, connect, pre-compute.

    Real-world use

    JavaScript prototype chain (Object.create), JPA entity detach/clone, FactoryBot/Faker test fixtures, Unity Prefabs, Kubernetes Job from PodSpec template, Monte-Carlo pricing engines cloning shared market data.

    Production case study

    Monte-Carlo pricing engine:

    • Scenario: 50k simulations per quote; 80% CPU reconstructing lookup tables.
    • Decision: One prototype context; clone with shared immutable references.
    • Outcome: Quote latency 4.2s → 380ms.
    • Lesson: profile construction before adding clone complexity.

    Trade-offs

    • Pro: skips expensive construction; runtime type via registry.
    • Pro: easy snapshotting for undo/replay.
    • Con: deep clone of complex graphs is delicate (cycles, handles).
    • Con: shared mutable state in clone → cross-instance bugs.

    Decision framework

    • Use when construction cost dominates — measure first.
    • Use for test fixtures with many near-identical instances.
    • Avoid when construction is cheap — clone adds complexity for nothing.
    • Avoid for objects with open sockets, file handles, DB connections.

    Best practices

    • Share immutable state across clones; copy mutable deltas only.
    • Use visited-set for deep clone with circular references.
    • structuredClone in JS for most values; hand-roll hot paths.

    Anti-patterns to avoid

    • Cloning mutable singleton prototype — all clones share mutations.
    • Prototype for cheap POJOs — Factory Method or constructor is simpler.
    • Deep clone when shallow + immutable shared refs suffices.

    Common mistakes

    • Forgetting to clone nested mutable arrays — alias bugs.
    • Cloning objects with non-clonable resources.
    • Registry prototype mutated by first clone — clone before customize.

    Debugging tips

    • Compare object identity of nested fields across clones — find accidental sharing.
    • Log construction vs clone time — verify Prototype earns its cost.

    Optimization strategies

    • Warmup registry at startup — pay construction before first request.
    • Shallow clone + copy-on-write for large immutable graphs.

    Common misconceptions

    • JS prototype chain ≠ GoF Prototype. JS is delegation; GoF is copy-based clone().
    • Prototype vs Memento: Prototype reuses template; Memento captures state for restore.

    Advanced interview questions

    Interview Prep

    Practice concise answers, then expand each card for the explanation.

    4 questions
    1IntermediateQuestionPrototype vs Factory Method?+

    Answer

    Factory constructs from scratch each time. Prototype clones pre-built instance — pays construction once.

    Follow-up

    Shared mutable state risk?
    2AdvancedQuestionDeep clone with circular refs?+

    Answer

    Track visited nodes in Map; reuse cloned reference when revisiting. structuredClone handles this in JS.

    Follow-up

    What does structuredClone refuse?
    3AdvancedQuestionJS prototype chain vs GoF Prototype?+

    Answer

    JS: lookup walks parent at runtime (delegation). GoF: clone() returns independent copy.

    Follow-up

    Object.create implements which?
    4IntermediateQuestionPrototype in test fixtures?+

    Answer

    Default object built once; tests override few fields — Prototype with fluent override DSL.

    Follow-up

    vs constructor with defaults?

    Summary

    You can choose Prototype over Factory Method, implement registry + clone safely, and explain deep vs shallow trade-offs. Tell the 1.4s → 9ms simulation story — if you can do that, you own Prototype.

    Ready to mark this lesson complete?Track your journey across the entire course.