If you remember five things
The whole pattern in one glance. Everything below is the defense of these five calls.
- Copy, do not construct. When setup is expensive or the exact config is only known at runtime, ask the object to clone itself instead of calling a constructor you may not even be able to write.
- Cloning is polymorphic. You hold a base reference (
Shape,Enemy) and callclone(); the concrete subtype copies itself. That is what makes it a pattern, not just a copy method. - The object owns the copy logic. The caller never needs to know how the thing was assembled, which is the whole point when the build steps live inside the object.
- Deep vs shallow is the landmine. Copy the top-level fields but share a nested mutable object and you silently corrupt the original and every other copy.
- A registry earns its keep. A map from a key to a ready-made prototype decouples "which preconfigured thing do I want" from "how was it built," and must always hand back a clone, never the stored original.
Where it fits. This is an LLD design pattern, and it shows up in rounds at the FAANG and senior-backend bar.
- Usually not by name, but as a "now make a thousand of these" follow-up.
- Sometimes a direct ask: "design copy-paste," "spawn game entities efficiently."
- Treat it as must-know: the trigger is subtle and the deep-copy question is guaranteed.
1The problem it solves#
Two different pains both point at the same answer. Picture a game with an enemy type called Orc.
Pain one: expensive setup, repeated.
- Building one orc is not cheap: load a mesh, pull textures off disk, parse an animation rig, bind a weapon loadout, roll stats.
- Call it 40 lines of setup and a few disk reads.
- A horde spawns and you need 300 orcs, all nearly identical except position.
- Calling that full constructor 300 times pays again and again for byte-for-byte identical work (mesh, textures, rig). Only 2 or 3 fields actually differ (position, maybe current HP).
Pain two: you do not know how to rebuild it.
- The object was assembled at runtime from a config file, a database row, or a sequence of user choices.
- Now you want another one just like it.
- You cannot write
new Orc(...)because you no longer know the arguments. - The knowledge of "how this thing was built" lives inside the object, not in your code.
Both roads lead to one move: ask the object to copy itself.
2The idea (mental model)#
The mental model I use is a rubber stamp.
- You carve the stamp once. That carving is the expensive part.
- Every impression after that is fast and identical.
- You can tweak each impression once you make it: write a name under it, change a color.
In code, the stamp is an already-built object we call the prototype.
- To make a new one, ask the prototype to
clone()itself. - The clone comes out pre-configured.
- Then you adjust the handful of fields that need to differ.
Two things make this a real pattern, not just "call a copy method."
- The cloning is polymorphic: you hold a base reference (
Shape,Enemy,Document) and callclone()without knowing the concrete class. The right subtype copies itself. - The object owns the copy logic: the caller never needs to know how the thing was assembled.
Shallow copy vs deep copy is the landmine, and interviewers love it. If you remember one thing, make it that: copying the top-level fields is easy, and deciding which nested objects to also copy is where the pattern is actually hard.
3Structure#
The cast is small.
- Prototype: an interface (or abstract class) that declares
clone(). - ConcretePrototype: the real classes that implement
clone()and return a copy of themselves. - Client: holds a prototype and calls
clone()when it needs a fresh copy. It does not care about concrete types.
There is usually a fourth piece: a registry (sometimes called a prototype manager).
- A map from a key like
"orc"or"circle"to a ready-made prototype instance. - The client asks the registry for a named prototype and clones what comes back.
- This is where the pattern earns its keep in real systems: it decouples "which preconfigured thing do I want" from "how was it built."
ShapeRegistry and the Shape interface, never to Circle or Rectangle. That arrow pattern is Dependency Inversion on the page.What the arrows buy you.
- Dependency Inversion: the client depends on the
Shapeabstraction, never on the concretes. - Open/Closed: add a new shape, implement
clone(), register it, and no existing code changes.
4Implementation in Java#
Skip Java's built-in Cloneable and Object.clone() on purpose. It is a famously awkward API, and saying so out loud is a signal.
clone()isprotected.- It throws a checked
CloneNotSupportedException. - It does a shallow copy by default.
- It bypasses constructors, so your invariants can quietly break.
- In an interview, define your own small contract instead. It reads better and shows you understand the copy rather than hiding it. Interviewers take the swap as a sign you have been burned by
Cloneablebefore.
Here is the shape example with a registry and a real deep copy.
interface Shape {
Shape clone();
void draw();
}
class Circle implements Shape {
private int radius;
private String color;
public Circle(int radius, String color) {
this.radius = radius;
this.color = color;
}
// copy constructor does the actual work
private Circle(Circle source) {
this.radius = source.radius;
this.color = source.color;
}
@Override
public Shape clone() {
return new Circle(this);
}
public void setRadius(int radius) { this.radius = radius; }
@Override
public void draw() {
System.out.println("Circle r=" + radius + " color=" + color);
}
}
Why a copy constructor behind clone().
- The copy constructor is where the copying actually happens;
clone()just delegates to it. - Deep-copy decisions live in one obvious place.
- A reviewer can audit every field in a single method.
Now the registry, plus a mutable field to make the deep-copy point concrete.
import java.util.*;
class Rectangle implements Shape {
private int width;
private int height;
private List<String> tags; // mutable reference, watch this
public Rectangle(int width, int height, List<String> tags) {
this.width = width;
this.height = height;
this.tags = tags;
}
private Rectangle(Rectangle source) {
this.width = source.width;
this.height = source.height;
// DEEP copy: new list, not the same reference
this.tags = new ArrayList<>(source.tags);
}
@Override
public Shape clone() { return new Rectangle(this); }
public List<String> getTags() { return tags; }
@Override
public void draw() {
System.out.println("Rect " + width + "x" + height + " tags=" + tags);
}
}
class ShapeRegistry {
private final Map<String, Shape> prototypes = new HashMap<>();
public void register(String key, Shape prototype) {
prototypes.put(key, prototype);
}
public Shape get(String key) {
Shape proto = prototypes.get(key);
if (proto == null) throw new NoSuchElementException(key);
return proto.clone(); // hand back a fresh copy, never the original
}
}
this.tags = source.tags instead of new ArrayList<>(...). Two clones and the registry's template now share one list. What happens the first time a clone adds a tag?Reveal the answer
a.getTags().add("mutated") mutates everybody, including the template, so every future clone is already corrupted on arrival. That is the exact bug the interviewer is fishing for, and the one-line new ArrayList<>(...) is the whole fix.And the client, showing why the deep copy matters.
public class Demo {
public static void main(String[] args) {
ShapeRegistry registry = new ShapeRegistry();
registry.register("small-circle", new Circle(5, "red"));
registry.register("tagged-rect",
new Rectangle(10, 20, new ArrayList<>(List.of("ui"))));
Rectangle a = (Rectangle) registry.get("tagged-rect");
Rectangle b = (Rectangle) registry.get("tagged-rect");
a.getTags().add("mutated");
a.draw(); // Rect 10x20 tags=[ui, mutated]
b.draw(); // Rect 10x20 tags=[ui] <- unaffected, good
}
}
Read the two output lines.
apicked up the new tag;band the stored template did not. That is correct.- With a shared reference,
a,b, and the prototype would all share one list and that oneaddwould corrupt all three. - The
new ArrayList<>(...)line is the fix, and it is worth narrating as you type it.
Here is the runtime flow when the client asks for a shape.
5A real-world example#
This pattern is everywhere once you know its shape.
- Game entity spawning. The horde of 300 orcs from section 1. Load one orc as a prototype at level-load time, register it, then clone it per spawn. Texture and mesh handles are shared on purpose: they are immutable, read-only resources and copying them would waste memory for no gain. Position and current HP are fresh per clone. This is the single most common reason game engines reach for Prototype.
- Design tools (Figma, Illustrator, PowerPoint). Copy-paste on a shape or a group is literally this. The selected objects are prototypes; paste clones them. Group copy forces you to deep-copy the children, the recursive version of our
tagslist: each child must clone its own mutable children too. - JavaScript objects and
structuredClone. The prototype-based object model is cut from this cloth, andstructuredClone()was added to the language because shallow copy (Object.assign, spread) kept biting people on nested data. - Config and request templates. Keep a fully-built default config or default HTTP request, then clone and tweak per call rather than rebuilding. Same with a seeded test fixture: build one valid record, clone it, change one field to trigger the exact case you want. Short tests, and the "only one field differs" intent stays obvious.
- Spreadsheets and document editors. Duplicating a slide or a styled row is prototype cloning with a deep copy. Miss the deep copy and editing the duplicate silently rewrites the original.
6When to use it, and when not to#
Two short lists you can recite in the room.
Reach for Prototype when:
- Creating an object is genuinely expensive (I/O, heavy computation, large allocation) and you need many near-identical copies. The 300-orc case.
- You do not know the concrete class at the point of creation, but you have an instance in hand.
- Configurations are many and you would rather carry preconfigured instances than a factory method per combination. Prototype often collapses 20 near-identical factory methods into one registry of 20 prototypes.
- You want to snapshot an object's current state and branch from it, for example an undo checkpoint.
Skip it when:
- Objects are cheap to construct. A plain constructor or a Builder is clearer. Do not clone a two-field DTO to look clever.
- The object graph is tangled with cycles, back-references, or resources that cannot be copied (open sockets, file handles, DB connections). Deep-copying that correctly is painful, error-prone, and sometimes impossible.
- The objects are immutable. If a thing never changes you do not need copies: share the one instance. Cloning immutables is pure waste, and Flyweight is the pattern that solves the immutable-sharing case instead.
Prototype
- How it makes things
- Copies an instance you already hold.
- Shines when
- Build is costly, or the config is only known at runtime.
- Cost to watch
- Deep vs shallow copy on every mutable field.
Builder
- How it makes things
- Constructs step by step from nothing.
- Shines when
- Construction has many optional parameters.
- Cost to watch
- You must know every argument at call time.
7Common mistakes#
The ways this pattern bites, roughly in order of how often they show up.
- Shallow copy when you needed deep. The number one failure. You copy the top-level fields but leave nested objects shared by reference. Everything looks fine until someone mutates a nested field through one copy and silently wrecks another. Decide per field: primitives and immutables can be shared, mutable sub-objects must be copied into new instances. Deep copy is recursive, so a cloned object must clone its mutable children too, all the way down.
- Leaning on Java's
Cloneable. It is a trap, as above.Object.clone()is shallow, protected, throwsCloneNotSupportedException, and skips your constructors so invariants can break. Use a copy constructor or your ownclone()contract and copy fields explicitly. You stay in control and the code reads honestly. - The registry handing back the original.
get()must returnproto.clone(), notproto. Return the stored prototype itself and the caller mutates your template, so every future clone is wrong. Clone on the way out, always. - Forgetting to copy down a subclass hierarchy. If a subclass adds fields, its
clone()(or copy constructor) must copy those too, typically by chaining to the superclass copy constructor first. A base-class copy constructor a subclass forgets to extend quietly drops the subclass state and produces a half-copied object. - Over-sharing "immutable" things that are not. You decide a field is safe to share because it is "basically constant," and it turns out to be mutable. Be honest about what is actually immutable:
finalfield, no setters, no mutable internals leaking through a getter.
Writing clone() and never mentioning deep vs shallow. If you copy a mutable collection by reference and move on without a word, the interviewer now assumes you will ship the shared-list bug in production. Narrate the copy depth as you type: that one sentence is the difference.
8Where it shows up in interviews#
Two shapes of question, and the tell for each.
Direct asks, common in LLD rounds.
- "Design a shape-drawing tool with copy-paste."
- "Spawn game entities efficiently."
- "Build an object cache that returns configured copies."
- All three are Prototype with a registry. Recognizing that fast is half the battle.
As a follow-up inside a bigger problem.
- You design something with heavy setup and the interviewer says "now I need a thousand of these, slightly different."
- That is your cue. Say the word "prototype," propose a registry of preconfigured instances, offer
clone().
The deep-vs-shallow question is coming, so get ahead of it. When you write clone(), say out loud: "primitives I copy by value, this mutable list I deep-copy into a new list, this immutable config I share on purpose." That single sentence signals senior-level care and usually ends the line of questioning before it starts.
Two comparisons worth having ready.
- Versus Builder: Builder constructs step by step from nothing and shines when construction has many optional parameters. Prototype copies something that already exists and shines when setup is expensive or unknown at compile time.
- Versus Factory Method: a factory decides which class to make; Prototype makes a copy of an instance you already hold. They compose well: a factory can pick a prototype from a registry and return a clone.
Builder builds, Factory chooses, Prototype copies. Say which one the setup cost is paying for and you have framed the whole creational-pattern trade-off in a sentence.
Mention thread safety if the registry is shared.
- If multiple threads call
get(), your clone must not expose shared mutable state. - Registration should happen before concurrent reads, or be properly synchronized: a
ConcurrentHashMap, or register-at-startup then treat as read-only. - Raising it unprompted shows you are thinking past the happy path.
Prototype trades the cost of building for the discipline of copying. The copy depth is where you earn or lose the room.
"Why not just use Java's Cloneable and Object.clone()?"
"My clone and the original keep corrupting each other. What went wrong?"
"The registry hands out a clone. Why not return the stored prototype directly?"
"A subclass adds fields. How do I make sure the copy stays complete?"
"The registry is shared across threads. Any concern?"
What you can now explain in the room
- When cloning beats constructing: expensive setup repeated, or a config you only know at runtime.
- Why the pattern needs polymorphic clone() and the object owning its own copy logic, not just a copy method.
- The shallow-vs-deep copy trap, and how to decide copy depth field by field.
- Why a registry must hand back proto.clone() and never the stored original.
- Why you skip Java's Cloneable and use a copy constructor behind your own clone() instead.
- How Prototype trades off against Builder and Factory Method, and where it hands off to Flyweight for immutables.
Next, look at the Builder and Factory Method guides to see how these three creational patterns trade off, and at the Flyweight pattern for the opposite idea: sharing instead of copying when objects are immutable.
Get a prep plan built around your Prototype Pattern interview
Tell us the role and your interview date. We will send back a dated, day by day plan. Free.