Prototype Pattern explained simply
Creating objects is usually cheap. You call new, set some fields, move on with your life. But sometimes it is not cheap at all. Sometimes creating a single object means loading a texture from disk, querying a database, parsing a config file, or running some expensive computation just to get the thing into a usable state.
Now imagine you need hundreds of those objects. Doing all that heavy initialization every single time? That is a waste.
The Prototype Pattern says: build the expensive object once, then just clone it whenever you need another one. Tweak the clone if you want. But skip the expensive setup.
Let’s dig in.
What is the Prototype Pattern?
- Creates new objects by cloning an existing instance (the prototype) instead of constructing from scratch.
- Avoids the cost of expensive initialization that would happen every time you call
new. - The client code does not need to know the concrete class – it just asks the prototype to copy itself.
In short:
Don’t build from scratch. Copy what you already have.
Real-Life Analogy
Think about document templates. When a law firm needs a new employment contract, nobody sits down and writes one from scratch every time. That would be insane – it takes hours to get all the legal language right, the formatting correct, the clauses in order.
Instead, they have a master template. Every new contract starts as a copy of that template. Then the lawyer fills in the client’s name, the salary, the start date, and maybe tweaks a clause or two.
The template is the prototype. Copying it is cloning. Filling in the specifics is customizing the clone.
Same thing in code. You set up an object with all the expensive defaults, register it as a prototype, and clone it whenever you need a new instance.
Class Diagram
Here is how the Prototype Pattern looks when applied to a game entity spawning system. The GameEntity declares the clone() method, concrete classes like Zombie and Dragon implement the cloning logic, and the EntityRegistry stores prototypes and hands out clones on demand.
Key Components
- Prototype (GameEntity): An abstract class or interface that declares the
clone()method. This is the contract – every prototype must know how to copy itself. - ConcretePrototype (Zombie, Dragon): Actual classes that implement the cloning logic. They know their own internals, so they know how to make a proper copy.
- Registry (EntityRegistry): Stores pre-built prototype instances and serves up clones on demand. The client never calls
new Zombie()directly – it asks the registry. - Client: The code that needs new objects. It talks to the registry, gets clones, and optionally customizes them.
The key insight is that the client is completely decoupled from the concrete classes. It does not know or care whether it is getting a Zombie or a Dragon. It just says “give me a clone of whatever is registered under this key.”
Example in Java – Game Entity Spawning System
This is a practical example where the Prototype Pattern really shines. In a game, you might have dozens of entity types, each requiring expensive setup – loading sprites from disk, calculating base stats from config files, initializing AI behavior trees. You do not want to repeat that work every time you spawn an enemy.
GameEntity Class (Prototype)
public abstract class GameEntity implements Cloneable {
protected int health;
protected double speed;
protected double positionX;
protected double positionY;
protected String sprite; // simulates an expensive-to-load resource
public abstract GameEntity clone();
public void setPosition(double x, double y) {
this.positionX = x;
this.positionY = y;
}
@Override
public String toString() {
return getClass().getSimpleName()
+ " [health=" + health + ", speed=" + speed
+ ", pos=(" + positionX + "," + positionY + ")"
+ ", sprite=" + sprite + "]";
}
}
Nothing fancy. The abstract class holds the common fields that every game entity has – health, speed, position, and a sprite. The clone() method is abstract, forcing each subclass to provide its own copy logic.
Zombie Class (ConcretePrototype)
public class Zombie extends GameEntity {
private int rotLevel;
public Zombie() {
// simulate expensive initialization
System.out.println("[EXPENSIVE] Loading zombie sprite from disk...");
try { Thread.sleep(100); } catch (InterruptedException e) { }
this.sprite = "zombie_sprite_v2.png";
System.out.println("[EXPENSIVE] Calculating zombie base stats...");
this.health = 80;
this.speed = 1.5;
this.rotLevel = 3;
this.positionX = 0;
this.positionY = 0;
}
// private constructor for cloning -- skips expensive setup
private Zombie(Zombie source) {
this.health = source.health;
this.speed = source.speed;
this.positionX = source.positionX;
this.positionY = source.positionY;
this.sprite = source.sprite;
this.rotLevel = source.rotLevel;
}
@Override
public GameEntity clone() {
System.out.println("[CHEAP] Cloning zombie...");
return new Zombie(this);
}
@Override
public String toString() {
return super.toString() + ", rotLevel=" + rotLevel;
}
}
Look at the two constructors. The public one does all the heavy lifting – loading sprites, calculating stats, simulating expensive I/O. The private copy constructor just copies field values. That is the whole point. The first Zombie is expensive. Every clone after that is nearly free.
Dragon Class (ConcretePrototype)
public class Dragon extends GameEntity {
private double fireBreathRange;
public Dragon() {
// simulate expensive initialization
System.out.println("[EXPENSIVE] Loading dragon sprite from disk...");
try { Thread.sleep(200); } catch (InterruptedException e) { }
this.sprite = "dragon_sprite_hd.png";
System.out.println("[EXPENSIVE] Calculating dragon base stats...");
this.health = 500;
this.speed = 8.0;
this.fireBreathRange = 15.0;
this.positionX = 0;
this.positionY = 0;
}
// private constructor for cloning
private Dragon(Dragon source) {
this.health = source.health;
this.speed = source.speed;
this.positionX = source.positionX;
this.positionY = source.positionY;
this.sprite = source.sprite;
this.fireBreathRange = source.fireBreathRange;
}
@Override
public GameEntity clone() {
System.out.println("[CHEAP] Cloning dragon...");
return new Dragon(this);
}
@Override
public String toString() {
return super.toString() + ", fireBreathRange=" + fireBreathRange;
}
}
Same idea. The dragon’s initial creation is even more expensive (200ms sleep to simulate loading that HD sprite), but cloning is instant.
EntityRegistry Class (Prototype Manager)
import java.util.HashMap;
import java.util.Map;
public class EntityRegistry {
private final Map<String, GameEntity> prototypes = new HashMap<>();
public void register(String key, GameEntity prototype) {
prototypes.put(key, prototype);
}
public GameEntity spawn(String key) {
GameEntity prototype = prototypes.get(key);
if (prototype == null) {
throw new IllegalArgumentException("No prototype registered for: " + key);
}
return prototype.clone();
}
}
The registry is dead simple. It holds a map of pre-built prototypes. When you call spawn("zombie"), it looks up the prototype and clones it. The caller gets a fresh copy without paying the initialization cost.
Putting It All Together
public class MainProgram {
public static void main(String[] args) {
// one-time setup: create the expensive prototypes
System.out.println("=== Initializing Prototypes (expensive, happens once) ===");
EntityRegistry registry = new EntityRegistry();
registry.register("zombie", new Zombie());
registry.register("dragon", new Dragon());
System.out.println("\n=== Spawning Entities (cheap clones) ===");
// spawn a horde of zombies -- no expensive loading
GameEntity z1 = registry.spawn("zombie");
z1.setPosition(10, 20);
GameEntity z2 = registry.spawn("zombie");
z2.setPosition(30, 40);
GameEntity z3 = registry.spawn("zombie");
z3.setPosition(50, 60);
// spawn a dragon
GameEntity d1 = registry.spawn("dragon");
d1.setPosition(100, 200);
System.out.println("\n=== Spawned Entities ===");
System.out.println(z1);
System.out.println(z2);
System.out.println(z3);
System.out.println(d1);
}
}
Output:
=== Initializing Prototypes (expensive, happens once) ===
[EXPENSIVE] Loading zombie sprite from disk...
[EXPENSIVE] Calculating zombie base stats...
[EXPENSIVE] Loading dragon sprite from disk...
[EXPENSIVE] Calculating dragon base stats...
=== Spawning Entities (cheap clones) ===
[CHEAP] Cloning zombie...
[CHEAP] Cloning zombie...
[CHEAP] Cloning zombie...
[CHEAP] Cloning dragon...
=== Spawned Entities ===
Zombie [health=80, speed=1.5, pos=(10.0,20.0), sprite=zombie_sprite_v2.png], rotLevel=3
Zombie [health=80, speed=1.5, pos=(30.0,40.0), sprite=zombie_sprite_v2.png], rotLevel=3
Zombie [health=80, speed=1.5, pos=(50.0,60.0), sprite=zombie_sprite_v2.png], rotLevel=3
Dragon [health=500, speed=8.0, pos=(100.0,200.0), sprite=dragon_sprite_hd.png], fireBreathRange=15.0
The expensive initialization happens exactly twice – once for the zombie prototype, once for the dragon prototype. After that, spawning is just copying fields. If you needed to spawn 500 zombies, you would still only load the sprite once.
When to Use the Prototype Pattern
- When object creation is significantly more expensive than copying (database lookups, file I/O, network calls, complex calculations).
- When you need to create objects at runtime but don’t know the concrete class in advance.
- When you want to avoid a parallel hierarchy of factory classes – one for each product type.
- When objects differ only slightly from each other. Clone and tweak is faster than build from scratch.
Advantages and Disadvantages
Advantages:
- Avoids expensive object creation. Build once, clone many times.
- Decouples the client from concrete classes. The client works with the prototype interface, not specific implementations.
- Simplifies object creation when you have many similar objects with slight variations.
- New entity types can be added at runtime by registering new prototypes – no code changes needed.
- Reduces the need for subclassing. Instead of creating a factory for each type, you just register a new prototype.
Disadvantages:
- Cloning objects with circular references or complex object graphs can be tricky.
- Deep copy vs shallow copy decisions can introduce subtle bugs if you get it wrong.
- Every class that wants to be a prototype must implement the cloning logic – this can be tedious for classes with many fields.
- Java’s
Cloneableinterface andObject.clone()are widely considered broken by design (we will talk about this below).
Shallow Copy vs Deep Copy
This is where the Prototype Pattern gets interesting – and where most bugs hide.
Shallow Copy duplicates the object’s fields as-is. Primitive values get copied. But object references? Those just get copied as pointers. The original and the clone end up sharing the same objects in memory.
Deep Copy duplicates everything – the object itself and all objects it references, recursively. The clone is completely independent.
Here is the difference in code:
import java.util.ArrayList;
import java.util.List;
public class Inventory implements Cloneable {
private String ownerName;
private List<String> items;
public Inventory(String ownerName, List<String> items) {
this.ownerName = ownerName;
this.items = items;
}
// SHALLOW copy -- items list is shared
public Inventory shallowCopy() {
return new Inventory(this.ownerName, this.items);
}
// DEEP copy -- items list is independent
public Inventory deepCopy() {
return new Inventory(this.ownerName, new ArrayList<>(this.items));
}
public List<String> getItems() { return items; }
@Override
public String toString() {
return ownerName + "'s inventory: " + items;
}
}
And the result:
public class CopyDemo {
public static void main(String[] args) {
List<String> items = new ArrayList<>();
items.add("Sword");
items.add("Shield");
Inventory original = new Inventory("Player1", items);
// shallow copy -- shares the items list
Inventory shallow = original.shallowCopy();
shallow.getItems().add("Potion");
System.out.println(original); // Player1's inventory: [Sword, Shield, Potion]
System.out.println(shallow); // Player1's inventory: [Sword, Shield, Potion]
// both changed! that's the bug.
System.out.println("---");
// reset
items = new ArrayList<>();
items.add("Sword");
items.add("Shield");
original = new Inventory("Player1", items);
// deep copy -- independent items list
Inventory deep = original.deepCopy();
deep.getItems().add("Potion");
System.out.println(original); // Player1's inventory: [Sword, Shield]
System.out.println(deep); // Player1's inventory: [Sword, Shield, Potion]
// only the clone changed. that's correct.
}
}
The rule of thumb: if your object contains mutable reference types (lists, maps, other objects), you almost always want a deep copy. If all your fields are primitives or immutable objects like String, a shallow copy is fine.
In our game entity example above, we used a copy constructor that copies all fields individually. Since String is immutable in Java and the rest are primitives, our clone is effectively a deep copy. But if GameEntity had a List<StatusEffect> activeEffects field, we would need to create a new list in the copy constructor – otherwise both the prototype and the clone would share the same status effects list, and adding a poison effect to one zombie would poison them all.
Real-World Use Cases
-
Java’s Object.clone(): The original mechanism for prototyping in Java. Implement
Cloneable, overrideclone(), and callsuper.clone(). It works, but it is considered a flawed API. Joshua Bloch in Effective Java recommends copy constructors or copy factory methods instead. TheCloneableinterface doesn’t even declare aclone()method – it just acts as a flag toObject.clone(). Weird design. -
Spring Bean Scopes (Prototype Scope): When you define a Spring bean with
@Scope("prototype"), the container creates a new instance every time the bean is requested – instead of returning the shared singleton. While Spring doesn’t literally callclone(), the concept is the same: the bean definition acts as a prototype that gets “copied” on each request. -
JavaScript’s Object.create(): This creates a new object using an existing object as the prototype. The new object inherits from the source object through the prototype chain. It is literally called
Object.create()for a reason. JavaScript’s entire inheritance model is prototype-based. -
Spreadsheet Cell Copying: When you copy a cell in Excel or Google Sheets, you are cloning a prototype. The formula, formatting, data validation rules, and conditional formatting all get copied. Then you modify what you need. Nobody re-enters all that from scratch.
-
Game Development: Unity’s
Instantiate()function is essentially the Prototype Pattern. You create a prefab (the prototype) in the editor with all its components, materials, scripts, and physics settings configured. At runtime,Instantiate()clones the prefab. Every enemy, bullet, and particle effect in most Unity games is spawned this way.
Wrapping Up
The Prototype Pattern is simple in concept but powerful in practice. When creating an object from scratch is expensive – disk I/O, network calls, complex initialization – you build it once and clone it from there.
The pattern shows up everywhere once you start looking for it. Prefabs in game engines, prototype-scoped beans in Spring, the entire JavaScript object system. It is one of those patterns that is so natural you have probably used it without knowing the name.
Just remember the shallow vs deep copy distinction. That is where the bugs live. If your object has mutable fields that reference other objects, make sure your clone is truly independent. A copy constructor that explicitly copies each field is usually the safest and most readable approach – cleaner than Java’s Cloneable mechanism and less error-prone.