Prototype Pattern explained simply
Thilan Dissanayaka Software Architecture February 14, 2020

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 Cloneable interface and Object.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, override clone(), and call super.clone(). It works, but it is considered a flawed API. Joshua Bloch in Effective Java recommends copy constructors or copy factory methods instead. The Cloneable interface doesn’t even declare a clone() method – it just acts as a flag to Object.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 call clone(), 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.

ALSO READ
Writing a Shell Code for Linux
Apr 21, 2020 Exploit Development

Shellcode is a small piece of machine code used as the payload in exploit development. In this post, we write Linux shellcode from scratch — starting with a simple exit, building up to spawning a shell, and explaining every decision along the way.

Exploiting a Stack Buffer Overflow on Windows
Apr 12, 2020 Exploit Development

In a previous tutorial we discusses how we can exploit a buffer overflow vulnerability on a Linux machine. I wen through all theories in depth and explained each step. Now today we are going to jump...

Exploiting a  Stack Buffer Overflow  on Linux
Apr 01, 2020 Exploit Development

Have you ever wondered how attackers gain control over remote servers? How do they just run some exploit and compromise a computer? If we dive into the actual context, there is no magic happening....

Basic concepts of Cryptography
Mar 01, 2020 Cryptography

Ever notice that little padlock icon in your browser's address bar? That's cryptography working silently in the background, protecting everything you do online. Whether you're sending an email,...

Common Web Application Attacks
Feb 05, 2020 Application Security

Web applications are one of the most targeted surfaces by attackers. This is primarily because they are accessible over the internet, making them exposed and potentially vulnerable. Since these...

Remote Code Execution (RCE)
Jan 02, 2020 Application Security

Remote Code Execution (RCE) is the holy grail of application security vulnerabilities. It allows an attacker to execute arbitrary code on a remote server — and the consequences are as bad as it sounds. In this post, we'll go deep into RCE across multiple languages, including PHP, Java, Python, and Node.js.

> > >