Creational Pattern Pattern Deep Dive

Prototype

Not every object is cheap to build. Some require a database round trip, a complex computation, or reading a large configuration file before they're ready to use. The Prototype pattern answers a very practical question: if you already have one fully-built object and need another one that's almost identical, why rebuild it from scratch at all — why not just copy it?

⚠️ The Problem

Imagine a game that spawns enemy orcs. Building one "orc" object from scratch means loading its base stats, its default equipment list, its AI behavior tree, and its mesh/animation references — expensive work if it happens every single time an orc appears. Now imagine a level that needs fifty orcs, each just a slight variant (a different starting position, maybe a different weapon). Re-running that entire expensive setup fifty times is wasteful, and worse, any subtle inconsistency in how each one gets constructed (a forgotten default, a slightly different code path) can produce orcs that are supposed to be identical but subtly aren't — a bug that's hard to notice until players report "some orcs feel wrong."

✅ The Solution

Keep one fully-configured "prototype" object around — a template orc, already built once — and give it a clone() (or copy()) method that knows how to produce a complete, independent duplicate of itself, including its nested data. Need a new orc? Clone the template and tweak just the handful of fields that should differ (position, weapon), instead of re-running the expensive setup logic. The object becomes responsible for knowing how to copy itself, so callers never need to know or duplicate its internal construction details.

Example 1 — spawning game enemies from a template

Primary Example — JavaScript
class Orc {
  constructor(stats, equipment, behaviorTree) {
    // Imagine this constructor is expensive: loading stats from a data file,
    // building a default loadout, compiling an AI behavior tree, etc.
    this.stats = stats;
    this.equipment = equipment;
    this.behaviorTree = behaviorTree;
    this.position = { x: 0, y: 0 };
  }

  // The object knows how to duplicate itself, including nested data
  clone() {
    const copy = new Orc(
      { ...this.stats },                 // deep copy of stats
      [...this.equipment],               // deep copy of equipment list
      this.behaviorTree                  // safe to share if truly immutable/shared
    );
    copy.position = { ...this.position }; // deep copy, not the same object reference
    return copy;
  }
}

// Built once, expensively, at level-load time
const orcTemplate = new Orc(
  { hp: 60, attack: 12, defense: 4 },
  ["Rusty Axe", "Leather Armor"],
  loadOrcBehaviorTree()
);

// Spawning 50 orcs: clone + tweak, never rebuild from scratch
const orcs = [];
for (let i = 0; i < 50; i++) {
  const orc = orcTemplate.clone();
  orc.position = { x: i * 2, y: 0 };
  orcs.push(orc);
}

orcs[0].stats.hp = 40; // wounding one orc does not affect orcTemplate or the other 49

Every spawned orc skips the expensive setup entirely — it's a copy of an already-built template, tweaked in place. Because clone() deep-copies stats, equipment, and position, mutating orcs[0].stats.hp after spawning has no effect on orcTemplate or any of the other 49 orcs — each clone is fully independent.

Example 2 — a secondary context: cloning a document template

Secondary Example — Python
import copy

class ReportTemplate:
    def __init__(self, title, sections, styling):
        # Imagine styling/sections were assembled from a slow-to-parse template file
        self.title = title
        self.sections = sections      # list of section dicts
        self.styling = styling        # dict of fonts, margins, colors

    def clone(self):
        # deepcopy ensures nested lists/dicts are copied, not shared by reference
        return copy.deepcopy(self)


# Built once from a slow-to-load formatted template file
quarterly_template = ReportTemplate(
    title="Quarterly Report",
    sections=[{"heading": "Summary", "body": ""}, {"heading": "Financials", "body": ""}],
    styling={"font": "Georgia", "margin_cm": 2.5, "accent_color": "#003366"},
)

# Producing a filled-in report: clone the template, then customize
q3_report = quarterly_template.clone()
q3_report.title = "Q3 2026 Report"
q3_report.sections[0]["body"] = "Revenue grew 12% quarter over quarter."

# The original template is untouched and ready to be cloned again for Q4
print(quarterly_template.sections[0]["body"])  # "" — unaffected by q3_report's edit

Same idea in a different domain: rather than re-parsing a template file and rebuilding the section/styling structures every time a new report is needed, clone() hands back an independent copy of an already-assembled template. Editing q3_report.sections afterward leaves quarterly_template untouched, because copy.deepcopy copied the nested list and dictionaries rather than sharing them.

👍 When To Use It

  • Creating an object from scratch is expensive — a database query, heavy computation, or parsing a large file — but you need several objects that are mostly identical to one you already have.
  • You want to avoid a big hierarchy of subclasses just to produce slightly different pre-configured variants — instead, keep one configured prototype per variant and clone it.
  • Objects need to be created at runtime based on a dynamically chosen example object, rather than a compile-time-known class name (useful in editors, games, and object-copy "paste" features).

👎 When NOT To Use It

  • When object construction is already cheap — cloning adds a copy step and a clone() method to maintain for no real benefit.
  • When the object graph contains resources that fundamentally cannot or should not be duplicated (an open file handle, a live network socket, a database connection) — cloning those naively produces a broken or dangerous second reference to the same external resource.
  • When deep-copying the object is itself complex or error-prone (e.g. cyclic references, or fields that intentionally must stay shared) — the "make cloning correct" work can end up costing more than the construction work it was meant to save.

❓ FAQ

What's the difference between a shallow copy and a deep copy, and why does Prototype usually need a deep copy?

A shallow copy duplicates an object's top-level fields, but if any field is itself an object (a list, a dict, a nested object), the copy just gets a reference to the same nested object rather than its own copy. A deep copy recursively copies those nested objects too, so the two top-level objects share nothing mutable. Prototype almost always needs deep copies: in the orc example, a shallow clone's equipment array would be the exact same array as the template's, so equipping one cloned orc with a new weapon (push) would silently show up on every other orc and the template itself — a hard-to-trace bug caused entirely by two "independent" clones secretly sharing state.

Is Prototype just a fancy word for "copy constructor"?

They're closely related — a copy constructor is one way to implement the copying Prototype relies on. What makes it "Prototype" as a pattern is the broader idea: objects expose a uniform clone()/copy() operation so that client code can create new instances by copying an existing one, without needing to know that object's concrete class or constructor signature at all — useful when you're cloning through a shared interface across many different concrete types.

Can Prototype replace a whole subclass hierarchy of preset variants?

Often, yes — instead of OrcWarrior, OrcArcher, OrcShaman subclasses each hardcoding their own stats, you can keep one pre-configured prototype instance per variant (a "warrior template," an "archer template") and clone whichever one you need. This trades a compile-time class hierarchy for a runtime registry of example objects, which is often more flexible when variants are data, not fundamentally different behavior.

🙋 There Are No Dumb Questions

Q: If I just need a copy, why not use my language's built-in object-copy utility instead of writing a clone() method?
A: For simple objects, a built-in deep-copy utility (like Python's copy.deepcopy, used in Example 2 above) is often exactly the right, minimal implementation of Prototype — you don't need to hand-write field-by-field copying every time. A custom clone() method earns its keep when copying needs special handling: skipping a field that shouldn't be duplicated (like a cache), regenerating a unique ID for the copy, or deliberately sharing one specific nested object (like a truly immutable, shared behavior tree) instead of deep-copying everything.

✅ Quick Check

Why does a Prototype's clone() method usually need to perform a deep copy rather than a shallow copy?


That wraps up the five Creational patterns. Next up: Adapter — the first Structural pattern, which tackles a different kind of problem entirely: how do you make two existing, incompatible interfaces work together without rewriting either one?

← Back to
Next →