Design Patterns - Creational

Prototype Pattern

Create new objects by cloning an existing object (prototype) rather than creating from scratch. Useful when object creation is expensive.

10 min readIntermediate

1What is the Prototype Pattern?

Real-World Analogy
Cell Division

Cells reproduce by dividing - creating a copy of themselves with all their properties. This is faster than building a new cell from scratch with all its complex structures.

Document Templates

Instead of creating a new contract from scratch, you copy a template and modify specific fields. The clone already has headers, formatting, and standard clauses.

Prototype Pattern creates new objects by copying an existing prototype instance. The client does not need to know the concrete class of the object being cloned. This is useful when object creation is costly (database calls, complex initialization) or when you need many similar objects.

Prototype Cloning

Prototype
Original Object
name: "Doc1"
config: complex
→ clone() →
Clone
New Object
name: "Doc2"
config: copied
Clone gets all properties without expensive initialization

2Shallow vs Deep Copy

Shallow Copy
Copies primitive values but shares references to nested objects. Changing nested object affects both original and clone.
Original.list == Clone.list
// Same reference!
Deep Copy
Recursively copies all nested objects. Original and clone are completely independent.
Original.list != Clone.list
// Different references!
Be Careful with Shallow Copy
If your object contains mutable nested objects (lists, maps, custom objects), a shallow copy will share these references. Modifying them in the clone affects the original. Use deep copy for complete independence.

3Implementation

Prototype Pattern - Document Cloning
// ========== Prototype Interface ==========
interface Prototype<T> {
    T clone();
}

// ========== Concrete Prototype ==========
class Document implements Prototype<Document> {
    private String title;
    private String content;
    private List<String> authors;
    private Map<String, String> metadata;

    public Document(String title, String content) {
        this.title = title;
        this.content = content;
        this.authors = new ArrayList<>();
        this.metadata = new HashMap<>();
        // Simulate expensive initialization
        System.out.println("Creating new document (expensive operation)...");
    }

    // Private constructor for cloning
    private Document(Document source) {
        this.title = source.title;
        this.content = source.content;
        // Deep copy collections
        this.authors = new ArrayList<>(source.authors);
        this.metadata = new HashMap<>(source.metadata);
        System.out.println("Cloning document (cheap operation)...");
    }

    @Override
    public Document clone() {
        return new Document(this);
    }

    // Setters
    public void setTitle(String title) { this.title = title; }
    public void addAuthor(String author) { authors.add(author); }
    public void addMetadata(String key, String value) { metadata.put(key, value); }

    @Override
    public String toString() {
        return "Document{title='" + title + "', authors=" + authors + "}";
    }
}

// ========== Prototype Registry (Optional) ==========
class DocumentRegistry {
    private Map<String, Document> prototypes = new HashMap<>();

    public void register(String key, Document doc) {
        prototypes.put(key, doc);
    }

    public Document get(String key) {
        Document prototype = prototypes.get(key);
        return prototype != null ? prototype.clone() : null;
    }
}

// ========== Usage ==========
public class Main {
    public static void main(String[] args) {
        // Create original document (expensive)
        Document template = new Document("Report Template", "Standard content...");
        template.addAuthor("Admin");
        template.addMetadata("version", "1.0");

        // Clone instead of create new (cheap)
        Document report1 = template.clone();
        report1.setTitle("Q1 Report");
        report1.addAuthor("Alice");

        Document report2 = template.clone();
        report2.setTitle("Q2 Report");
        report2.addAuthor("Bob");

        System.out.println(report1);
        System.out.println(report2);

        // Using registry
        DocumentRegistry registry = new DocumentRegistry();
        registry.register("contract", new Document("Contract", "Legal text..."));

        Document contract1 = registry.get("contract");
        contract1.setTitle("Contract with Acme Corp");
    }
}
Output:
Creating new document (expensive operation)...
Cloning document (cheap operation)...
Cloning document (cheap operation)...
Document{title='Q1 Report', authors=[Admin, Alice]}
Document{title='Q2 Report', authors=[Admin, Bob]}

4When to Use Prototype

Expensive Initialization
When creating objects requires DB calls, file reads, or complex calculations.
Many Similar Objects
When you need many objects with slight variations from a base configuration.
Avoid Subclasses
When you want to avoid creating a hierarchy of factory classes.
Runtime Configuration
When object types are determined at runtime based on user input.

5Interview Questions

What is the Prototype pattern?
Prototype creates new objects by cloning an existing prototype instance. Instead of calling a constructor, you call clone() on an existing object. This is useful when object creation is expensive or when you need many objects with similar initial state.
What is the difference between shallow and deep copy?
Shallow copy copies primitive values and references (nested objects are shared). Deep copy recursively copies all nested objects (completely independent). For Prototype pattern with mutable nested objects, use deep copy to avoid unintended side effects.
How does Java's clone() method work?
Java's Object.clone() performs a shallow copy. To use it, implement Cloneable interface. For deep copy, override clone() and manually copy nested objects. Many prefer copy constructors or serialization for cloning due to clone()'s design issues.

6Key Takeaways

1Prototype creates objects by cloning existing instances.
2Useful when object creation is expensive.
3Shallow copy shares references; deep copy is independent.
4Prototype Registry can manage frequently-cloned prototypes.
5Alternative to complex factory hierarchies.
6Use copy constructor or deep copy library for reliable cloning.