Prototype Pattern
Clone existing objects instead of constructing from scratch. An intermediate creational pattern — the answer when building an object is expensive but copying one is cheap.
Why Prototype Exists
You are building a game engine. Each level has hundreds of enemies — orcs, goblins, dragons — each configured with health, speed, attack power, equipped weapons, spawn position, AI behaviour tree, and a sprite sheet loaded from disk. Creating one enemy is expensive: parse the config file, load the sprite from disk, compile the shader, build the AI tree. Now multiply that by 200 enemies per level. The naive approach constructs each enemy from scratch, repeating the same expensive setup for every orc that shares 95% of its configuration with every other orc.
What goes wrong:
- Repeated expensive setup — disk I/O, GPU compilation, and AI tree parsing happen 50 times for identical orcs; only spawn position differs
- Tight coupling to concrete classes — the spawning code must know every constructor argument and initialization step for every enemy type
- Can’t create objects at runtime from unknown types — if a level editor adds a new enemy type via config, the code can’t
newa class it doesn’t know at compile time - Scattered initialization logic — the 7-step setup is duplicated everywhere enemies are created; changing orc health means editing every spawn site
This is the problem Prototype solves — build one fully-configured “master copy” of each enemy type, then clone it whenever you need a new instance. The clone copies the already-loaded sprite, already-compiled shader, and already-built AI tree in memory — no disk I/O, no GPU compilation, no parsing. Tweak only what differs (spawn position), and you have a new enemy in microseconds instead of milliseconds.
What Is Prototype?
Prototype is a creational pattern that creates new objects by cloning an existing instance (the prototype) instead of constructing from scratch. Each prototype knows how to copy itself. The client asks the prototype “give me a copy of yourself,” receives an independent clone, and tweaks only what’s different. The client never needs to know the concrete class, the constructor arguments, or the initialization sequence — it just calls clone().
— Gamma, Helm, Johnson, Vlissides (1994)
Key insight: Prototype shifts the creation question from “which class should I instantiate?” to “which instance should I copy?” This is fundamentally different from Factory Method (which decides the class) or Builder (which assembles step by step). Prototype doesn’t care about classes or construction steps — it only needs a live, configured object and a way to duplicate it. This makes it uniquely powerful for dynamic systems where object types are defined at runtime (game editors, visual builders, document templates).
clone() method that copies its own stateprototype.clone() instead of new ConcreteClass(...)"orc" → master orc, "dragon" → master dragonParticipants & Structure
| Participant | Role | In the Analogy |
|---|---|---|
| Prototype | An interface (or abstract class) that declares a clone() method. Every cloneable object must implement this contract. | The concept of “a cell that can divide itself.” |
| Concrete Prototype | Implements clone() by copying its own fields into a new instance. Handles deep-copy logic for mutable internal state (lists, maps, nested objects). | A specific cell type (red blood cell, neuron) that knows how to replicate its own organelles. |
| Prototype Registry (optional) | A map of String → Prototype that stores pre-configured master copies. The client looks up a prototype by name and clones it. New types can be registered at runtime. | A stem-cell bank — labelled vials of master cells ready to be cloned on demand. |
| Client | Asks the registry (or a prototype directly) for a clone. Receives an independent copy and customises only what differs. Never calls new ConcreteClass(). | The body — requests “give me another red blood cell” without knowing how to build one from scratch. |
- A shallow clone copies field values directly — if a field is a reference (e.g. a
List), both the original and clone share the same list object. - Mutating one corrupts the other.
- A deep clone recursively copies all mutable internal objects so the clone is fully independent.
- Always deep-clone mutable fields in your
clone()implementation.
The Pattern In Motion
Scenario: A game level spawner that needs 50 orcs. Instead of constructing each from scratch, it clones a pre-built master orc and tweaks the spawn position.
"orc".registry.put("orc", masterOrc), registry.put("dragon", masterDragon)Map<String, Prototype>. New types can be added at runtime — a level editor can register a custom enemy without recompiling.registry.get("orc").clone()clone() creates a new OrcPrototype with all fields copied (deep clone for mutable state). No disk I/O, no GPU work — pure memory copy.clone.setPosition(randomSpawn())registry.get("goblin").clone() — same pattern, different key.- Java’s
new ArrayList<>(existingList)is a copy constructor — it works, but the caller must know the concrete class. - Prototype is polymorphic: the client calls
clone()on the interface and gets the right concrete type without knowing it. - This is critical when types are determined at runtime.
You Already Use This
Prototype is baked into the Java language itself via java.lang.Cloneable and Object.clone(). While the JDK’s cloning mechanism has well-known design flaws, the pattern appears throughout the standard library.
java.lang.Object.clone() — the native prototype mechanism. Any class implementing Cloneable can override clone() to produce a field-by-field copy. Object.clone() performs a shallow copy by default — you must override it for deep cloning of mutable fields. java.util.ArrayList copy constructor — new ArrayList<>(source) creates a shallow clone of the list. The new list is independent (adding/removing elements doesn’t affect the original), but the elements themselves are shared references. This is prototype-style creation: copy an existing collection rather than building one from scratch. java.util.Date โ implements Cloneable. Defensive copying in the JDK often uses (Date) date.clone() to prevent callers from mutating internal state. This is Prototype used for safe snapshot creation. Cloneable is considered broken: - Josh Bloch (Effective Java) calls
Cloneablea “broken” interface — it has no methods,clone()is onObject, and it returnsObjectrequiring a cast. - Modern Java code often prefers a copy constructor or a static factory like
Enemy.from(existingEnemy)instead ofCloneable. - The pattern is the same — clone an existing instance — but the mechanism is cleaner.
Build It Once
Domain: Game Character Cloning. A Prototype interface with clone(), two concrete prototypes (Warrior, Mage), a PrototypeRegistry, and a spawner that clones from the registry.
- Notice how
Warriorhas a private no-arg constructor used only byclone(). - The public constructor does expensive init (sprite loading, shader compilation).
- The private one skips it — clones get a cheap path. This is the key performance trick of Prototype.
Common Mistakes
clone() copies a List reference instead of creating a new list, the clone and the original share the same list. Adding a skill to the clone adds it to the master — corrupting every future clone. Always deep-copy mutable fields: copy.skills = new ArrayList<>(this.skills).
- If
clone()uses the same constructor as the public factory path, the clone pays the full initialization cost (disk I/O, GPU work). - Use a private no-arg constructor that skips expensive setup.
- Clones copy already-loaded resources from the prototype — they should never reload them.
- Deep cloning isn’t just one level deep.
- If your
List<Weapon>contains mutableWeaponobjects, copying the list isn’t enough — you must also clone eachWeaponinside it. - Otherwise, the clone and the original share the same
Weaponinstances. - Rule: recursively clone every mutable object in the graph.
- If creating an object takes microseconds (no I/O, no parsing, no complex setup), cloning adds unnecessary complexity.
- Prototype shines when construction is genuinely expensive — disk I/O, network calls, GPU compilation, deep object graphs.
- For simple POJOs, just use
new.
When To Use Prototype
- Object construction is expensive — disk I/O, network calls, GPU work, deep object graphs — but copying is cheap
- You need many similar instances that differ in only a few fields — clone the master and tweak
- Object types are determined at runtime — loaded from config, created in a visual editor, defined by users — and you can’t hardcode
newcalls - You want to decouple the client from concrete classes — the client knows only the
Prototypeinterface - You need a snapshot/undo mechanism — clone the current state to save it, restore by cloning the saved copy
- Objects are cheap to construct — simple POJOs with no I/O or complex setup;
newis simpler - Objects have deeply nested mutable graphs that make
clone()error-prone and hard to maintain - You need to decide which class to instantiate at runtime — use Factory Method instead
- You need to create families of products — use Abstract Factory instead
| Pattern | Creates by | Focus | When to pick it |
|---|---|---|---|
| Prototype ← this | Cloning an existing instance | Copy instead of construct | Construction is expensive; copying is cheap |
| Factory Method | Subclass overriding a method | Which class to instantiate | Decision on type is at compile time via inheritance |
| Abstract Factory | Factory interface for families | Which family of products | Products must come from the same family |
| Builder | Step-by-step fluent API | How to assemble one complex object | Many optional fields, validation needed |
Problems To Solve
Prototype problems test whether you can implement deep cloning correctly, use a registry for runtime type discovery, and avoid the shallow-copy trap.
| Difficulty | Problem | Key Insight |
|---|---|---|
| Easy | Document Template Cloner Build a Document prototype with title, body text, and a list of tags. The user selects a template (e.g. “Invoice”, “Report”), clones it, and fills in the details. Verify that changing the clone’s tags list doesn’t affect the template. | Tests the basic clone + deep-copy of a mutable list. The registry holds 2–3 templates. The trap: shallow-copying the tags list means all clones share the same list. |
| Medium | Shape Editor with Undo Build a drawing app with Circle, Rectangle, and Line shapes. Each shape has position, colour, and stroke. Implement “duplicate shape” (clone + offset) and “undo” (clone state before each edit, push to undo stack). All shapes share a Shape interface with clone(). | Tests polymorphic cloning (client clones Shape without knowing the concrete type) and using Prototype for undo snapshots. The tricky part: clone() must return the correct concrete type. |
| Medium | Config-Driven Enemy Spawner Enemy types are defined in a JSON config file (not in code). At startup, parse each entry into a prototype and register it. The spawner clones from the registry by key. Add a new enemy type by editing the config — no recompilation. | Tests the runtime type discovery power of Prototype. The registry is populated dynamically; the spawner never names a concrete class. This is impossible with Factory Method alone — you’d need to edit the factory for each new type. |
| Hard | Deep Graph Cloning Build a Company object containing Department objects, each containing Employee objects. Employees have mutable Address objects. Implement clone() that deep-copies the entire graph. Prove independence: mutating a cloned employee’s address must not affect the original company. | Tests recursive deep cloning of a 4-level object graph. Every mutable object at every level must be cloned. The test: clone.getDept(0).getEmployee(0).getAddress().setCity("X") must not change the original. This is where Prototype gets hard — and where serialization-based cloning (serialize โ deserialize) becomes an alternative. |
- When asked about Prototype, the interviewer wants to see: (1) a
clone()method on the Prototype interface that returns the Prototype type; (2) deep-copy logic for all mutable fields (not justsuper.clone()); (3) a registry for runtime type lookup; (4) a clear explanation of when to use it (expensive construction) vs. - when not to (simple objects).
- Bonus: mention that Java’s
Cloneableis broken and modern code prefers copy constructors or static factory methods.