SSourav Saha
HomeExperienceSoftware DesignSystem DesignLearningBooksToolsContact
SSourav Saha

Building scalable backend systems, distributed infrastructure, and cloud-native applications.

Navigation

  • Home
  • Experience
  • Software Design
  • System Design
  • Learning

More

  • Books
  • Tools
  • Contact

Connect

  • LinkedIn
  • Email

© 2026 Sourav Saha. All rights reserved.

Built with using Next.js

Software DesignPrototype Pattern
Software DesignCreational Patterns

Prototype Pattern

Create new objects by cloning an existing instance instead of constructing from scratch — ideal for expensive or pre-configured objects.

January 13, 20255 min read
design-patternsjavaprototypecreational

One-line Definition

Encapsulates object cloning so clients can duplicate complex objects without depending on their concrete classes.


Problem

Suppose creating an object requires a heavy database query, complex calculations, or network requests. If you need multiple similar instances, running that expensive setup logic every time is inefficient. Additionally, if the client tries to copy an object manually, it must know all the object's internal fields, breaking encapsulation. The Prototype Pattern delegates the cloning process to the objects themselves.


Real-world Analogy

Imagine you've spent an hour customizing a resume template in a word processor. When you need a version for a different job, you don't start from a blank page — you duplicate the file and change just the job title and cover letter. The prototype is your original document, and each clone is a new variant.


Structure

  • Client
    • Requests a new object by asking an existing prototype to clone itself.
  • Prototype
    • Interface declaring a clone() method.
  • ConcretePrototype
    • Implements clone() by copying its own fields into a new instance.

Diagram

classDiagram
    class Client {
    }
    class Prototype {
        <<interface>>
        +clone(): Prototype
    }
    class ConcretePrototype {
        -field1
        +clone(): Prototype
    }
    
    Client --> Prototype : calls clone()
    ConcretePrototype ..|> Prototype

Code Walkthrough

Notice that Main (the client) duplicates objects by calling clone(). The client never has to manually copy internal state or rerun expensive setups.

abstract class Shape implements Cloneable {
    private String color;
    private int x;
    private int y;

    public Shape() {}

    public Shape(Shape source) {
        this.color = source.color;
        this.x = source.x;
        this.y = source.y;
    }

    public abstract Shape clone();

    public void setColor(String color) { this.color = color; }
    public void setX(int x) { this.x = x; }
    public void setY(int y) { this.y = y; }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "{color='" + color + "', x=" + x + ", y=" + y + "}";
    }
}

class Circle extends Shape {
    private int radius;

    public Circle() {}

    public Circle(Circle source) {
        super(source);
        this.radius = source.radius;
    }

    public void setRadius(int radius) { this.radius = radius; }

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

class Rectangle extends Shape {
    private int width;
    private int height;

    public Rectangle() {}

    public Rectangle(Rectangle source) {
        super(source);
        this.width = source.width;
        this.height = source.height;
    }

    public void setWidth(int width) { this.width = width; }
    public void setHeight(int height) { this.height = height; }

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

class Main {
    public static void main(String[] args) {
        Circle original = new Circle();
        original.setColor("Red");
        original.setX(10);
        original.setY(20);
        original.setRadius(15);

        Circle cloned = (Circle) original.clone();
        cloned.setColor("Blue");

        System.out.println("Original: " + original);
        System.out.println("Cloned:   " + cloned);

        Rectangle rect = new Rectangle();
        rect.setColor("Green");
        rect.setWidth(100);
        rect.setHeight(50);

        Rectangle rectClone = (Rectangle) rect.clone();
        rectClone.setX(200);

        System.out.println("Original Rect: " + rect);
        System.out.println("Cloned Rect:   " + rectClone);
    }
}

Bad vs Good

Bad Approach

Problems

  • Repeated expensive construction logic for similar objects.
  • Client is tightly coupled to concrete classes.
  • Hard to duplicate objects if fields are private.
class DocumentService {
    public Document createReport() {
        Document doc = new Document();
        doc.setFont("Arial");
        doc.setFontSize(12);
        doc.setMarginTop(20);
        // ... duplicated heavy setup ...
        doc.setHeaderTemplate(loadFromDB("report-header"));
        return doc;
    }

    public Document createInvoice() {
        Document doc = new Document();
        doc.setFont("Arial");
        doc.setFontSize(12);
        doc.setMarginTop(20);
        // ... duplicated heavy setup ...
        doc.setHeaderTemplate(loadFromDB("invoice-header"));
        return doc;
    }
}

Better Approach

Improvements

  • Expensive setup happens only once during prototype creation.
  • New variants are added by cloning the prototype, keeping business logic unchanged.
class DocumentService {
    private final Document basePrototype;

    public DocumentService() {
        basePrototype = new Document();
        basePrototype.setFont("Arial");
        basePrototype.setFontSize(12);
        basePrototype.setMarginTop(20);
    }

    public Document createReport() {
        Document doc = basePrototype.clone();
        doc.setHeaderTemplate(loadFromDB("report-header"));
        return doc;
    }

    public Document createInvoice() {
        Document doc = basePrototype.clone();
        doc.setHeaderTemplate(loadFromDB("invoice-header"));
        return doc;
    }
}

Pros vs Cons

ProsCons
Avoids expensive repeated constructionDeep cloning complex object graphs is difficult
Eliminates duplicated setup codeMust implement clone() across the entire class hierarchy
Can maintain a registry of pre-configured prototypesCircular references make cloning error-prone
Decouples client from concrete classesJava's built-in Cloneable interface is notoriously flawed
Allows adding or removing objects at runtimeShallow copies can lead to unexpected side effects

When to Use

  • Construction of an object is computationally expensive or requires heavy IO.
  • You have many similar objects differing only slightly in state.
  • You want to avoid a massive hierarchy of factories parallel to a hierarchy of products.
  • You need to duplicate objects without coupling your code to their concrete classes.

When Not to Use

  • Objects are cheap to create using a constructor.
  • The objects do not share significant initial state.
  • Dealing with deep copies of complex object graphs outweighs the performance benefits.

Real-world Examples

  • java.lang.Object#clone() (The basic Java implementation)
  • Custom copy constructors or copy() methods in immutable classes
  • Spring's @Scope("prototype") (Though this acts more like a factory, the intent is related)

Key Takeaway

The Prototype Pattern delegates object duplication to the objects themselves, allowing clients to clone complex structures without knowing their concrete types. Use it to bypass expensive construction logic when objects share most of their state, but be incredibly careful about the distinction between shallow and deep copying.

PreviousSingleton PatternNextAbstract Factory Pattern