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
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
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?