Back to Creational: Singleton, Factory, Builder, Prototype
Q5Question 5 of 12

What is the Prototype design pattern, its use cases, and example

Key Concepts
  • Prototype creates new objects by copying a fully configured instance, not constructing from scratch.
  • Use cases: expensive construction, pre-configured templates, runtime type selection, snapshotting.
  • Prefer a copy constructor or copy() method over Cloneable for safety and clarity.
  • Shallow copy shares inner mutable references; deep copy gives full independence.
  • For banking order templates, copy the template and change only the symbol and quantity.
  • Immutable objects need no copying – they can be safely shared.

Definition

Prototype creates a new object by copying a ready-made template, saving the cost of building from scratch and preserving shared defaults.
  • Prototype pattern – a creational pattern that creates new objects by copying an existing instance (the prototype) instead of constructing from scratch.
  • Use cases – expensive construction, pre-configured templates, runtime type selection, snapshotting.
  • Implementation – copy constructor or copy() method; avoid Cloneable and native clone().
  • Shallow vs deep – shallow copies share inner mutable objects; deep copies are fully independent.
  • Key rule – always deep-copy mutable fields to avoid the shared-state trap.

At the National Bank of India there is a standard pre-filled order template with the exchange, the default fee rate, and the settlement cycle already set. When Rajesh places an order, the clerk does not fill a blank form from scratch; they photocopy the standard template and just change the stock symbol and quantity. The master template is the prototype, and each photocopy is a new independent object.

Understanding the Concepts

What is the Prototype pattern?

The core idea is that you keep fully configured prototype objects, and when you need a new object you copy a prototype rather than building a fresh one. The new object starts as a duplicate and you adjust only the fields that differ.

JAVA
package com.bank.model;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public class OrderTemplate {
    private String exchange;
    private BigDecimal defaultFee;
    private String settlementCycle;
    private List<String> tags;

    public OrderTemplate(String exchange, BigDecimal defaultFee,
                         String settlementCycle, List<String> tags) {
        this.exchange = exchange;
        this.defaultFee = defaultFee;
        this.settlementCycle = settlementCycle;
        this.tags = new ArrayList<>(tags);
    }

    // Copy constructor: deep copy
    public OrderTemplate(OrderTemplate other) {
        this.exchange = other.exchange;
        this.defaultFee = other.defaultFee;
        this.settlementCycle = other.settlementCycle;
        this.tags = new ArrayList<>(other.tags);
    }

    public OrderTemplate copy() {
        return new OrderTemplate(this);
    }

    public void setExchange(String exchange) { this.exchange = exchange; }
    public void addTag(String tag) { this.tags.add(tag); }

    @Override
    public String toString() {
        return exchange + " fee=" + defaultFee + " cycle=" + settlementCycle + " tags=" + tags;
    }
}

What are the use cases for Prototype?

Four concrete use cases:

  1. Expensive construction: building an object requires a database call or heavy computation. Cloning avoids repeating that cost.
  2. Pre-configured templates: most objects share a baseline configuration. Clone the template and tweak.
  3. Runtime type selection: you hold an object and need another of the same concrete type without knowing the type statically.
  4. Snapshotting: clone captures the current state so later changes do not affect the snapshot.
JAVA
package com.bank.demo;

import java.math.BigDecimal;
import java.util.List;

public class PrototypeUseCases {
    public static void main(String[] args) {
        // Use case 2: pre-configured template
        OrderTemplate nseTemplate = new OrderTemplate(
            "NSE", new BigDecimal("20.00"), "T+1", List.of("DEFAULT")
        );

        // Clone and customise for each order
        OrderTemplate rajeshOrder = nseTemplate.copy();
        rajeshOrder.addTag("RAJESH-ORDER");

        OrderTemplate priyaOrder = nseTemplate.copy();
        priyaOrder.addTag("PRIYA-ORDER");

        System.out.println("Template: " + nseTemplate);
        System.out.println("Rajesh:   " + rajeshOrder);
        System.out.println("Priya:    " + priyaOrder);
    }
}
TEXT
Template: NSE fee=20.00 cycle=T+1 tags=[DEFAULT]
Rajesh:   NSE fee=20.00 cycle=T+1 tags=[DEFAULT, RAJESH-ORDER]
Priya:    NSE fee=20.00 cycle=T+1 tags=[DEFAULT, PRIYA-ORDER]

Each order is independent. Adding a tag to Rajesh's order does not affect the template or Priya's order.

How do you implement deep copy correctly?

Always deep-copy mutable fields. Immutable fields like String and BigDecimal need no copy.

JAVA
// Deep copy: create new list
this.tags = new ArrayList<>(other.tags);

// If tags contained mutable objects, copy each one too:
// this.legs = other.legs.stream()
//     .map(Leg::new)  // copy constructor for each leg
//     .collect(Collectors.toList());

What is the difference between shallow and deep copy?

| Aspect | Shallow Copy | Deep Copy |
|---|---|---|
| Top-level fields | Copied | Copied |
| Nested mutable objects | Shared references | Independent copies |
| Mutating copy affects original? | Yes | No |
| Safety | Dangerous for mutable state | Safe |

Why avoid Cloneable?

Cloneable is a marker interface with no clone method. Object.clone() produces a shallow copy by default, bypasses constructors, cannot assign final fields, and throws a checked exception. Use a copy constructor instead.

JAVA
// AVOID:
public class BadOrder implements Cloneable {
    @Override
    public BadOrder clone() {
        try {
            return (BadOrder) super.clone(); // shallow!
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

// PREFER:
public class GoodOrder {
    public GoodOrder(GoodOrder other) {
        // explicit deep copy
    }
}

What are common mistakes with Prototype?

  • Shallow copy that accidentally shares mutable state: changing one object silently changes another.
  • Using Cloneable without overriding clone to deep-copy mutable fields.
  • Forgetting that copying an immutable object is unnecessary: just share the reference.

How does Prototype interact with Java records?

Java records are immutable and shallow by nature: all fields are final. A record's wither pattern (creating a new record with one field changed) is the idiomatic replacement for clone. Since records cannot be mutated, the shallow-copy trap does not apply to their primitive and String fields, but mutable reference fields like List still need defensive copying.

JAVA
package com.bank.model;

import java.math.BigDecimal;
import java.util.List;

public class RecordPrototype {
    record AccountTemplate(String type, BigDecimal defaultBalance, List<String> features) {
        // Wither pattern: create a copy with one field changed
        AccountTemplate withType(String newType) {
            return new AccountTemplate(newType, defaultBalance, List.copyOf(features));
        }
    }

    public static void main(String[] args) {
        AccountTemplate savings = new AccountTemplate(
            "SAVINGS", new BigDecimal("1000.00"), List.of("ATM", "NetBanking"));
        AccountTemplate current = savings.withType("CURRENT");

        System.out.println("Original: " + savings);
        System.out.println("Copy: " + current);
        System.out.println("Different objects: " + (savings != current));
    }
}
TEXT
Expected output:
Original: AccountTemplate[type=SAVINGS, defaultBalance=1000.00, features=[ATM, NetBanking]]
Copy: AccountTemplate[type=CURRENT, defaultBalance=1000.00, features=[ATM, NetBanking]]
Different objects: true

What should a senior engineer know?

  • In modern Java, records with a compact copy helper make Prototype cleaner.
  • Prototype pairs well with a registry: keep named prototypes in a Map and clone by name.
  • For banking what-if scenarios, Prototype lets you simulate without touching real data.
JAVA
package com.bank.service;

import java.util.HashMap;
import java.util.Map;

public class OrderTemplateRegistry {
    private final Map<String, OrderTemplate> prototypes = new HashMap<>();

    public void register(String name, OrderTemplate prototype) {
        prototypes.put(name, prototype);
    }

    public OrderTemplate create(String name) {
        OrderTemplate prototype = prototypes.get(name);
        if (prototype == null) throw new IllegalArgumentException("Unknown: " + name);
        return prototype.copy(); // deep copy
    }
}

Visual Overview

DIAGRAM
  Prototype Pattern Flow
  ──────────────────────

  ┌─────────────────────────┐
  │  OrderTemplate (NSE)    │  ◄── prototype
  │  exchange = NSE         │
  │  fee = 20.00            │
  │  cycle = T+1            │
  │  tags = [DEFAULT]       │
  └────────┬────────────────┘
           │ .copy() (deep)
     ┌─────┼──────┐
     ▼            ▼
  ┌──────────┐  ┌──────────┐
  │ Rajesh's │  │ Priya's  │
  │ Order    │  │ Order    │  ◄── independent copies
  │ tags:    │  │ tags:    │
  │[DEFAULT, │  │[DEFAULT, │
  │ RAJESH]  │  │ PRIYA]   │
  └──────────┘  └──────────┘

  Template is UNTOUCHED

Key Takeaway

The Prototype pattern creates new objects by copying an existing, fully configured instance rather than constructing from scratch. It pays off when construction is expensive, when most objects share a baseline configuration, or when you need another object of the same concrete type at runtime.

At National Bank of India, Rajesh keeps one standard order template per exchange with the default fee rate and settlement cycle already set. For each new order he copies the template and changes only the symbol, quantity, and account. This saves re-entering the shared defaults and is faster under heavy load at market open.

The most important detail is shallow versus deep copy. A shallow copy shares references to nested mutable objects, so changing the copy silently changes the original. Rajesh always uses a copy constructor that creates new lists and copies each mutable field, keeping every order independent.

Rajesh avoids Cloneable because it is a marker interface with no clone method, produces a shallow copy by default, bypasses constructors, cannot assign final fields, and throws checked exceptions. A copy constructor is explicit, type-safe, and deep-copy logic is visible. For immutable objects, copying is unnecessary since sharing one instance is safe.

Remember one sentence: Prototype deep-copies an existing template to create new objects, and a copy constructor beats Cloneable because it is explicit, type-safe, and works with final fields.

---

Share
Use ← → arrow keys to navigate