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.
- Interface declaring a
- ConcretePrototype
- Implements
clone()by copying its own fields into a new instance.
- Implements
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
| Pros | Cons |
|---|---|
| Avoids expensive repeated construction | Deep cloning complex object graphs is difficult |
| Eliminates duplicated setup code | Must implement clone() across the entire class hierarchy |
| Can maintain a registry of pre-configured prototypes | Circular references make cloning error-prone |
| Decouples client from concrete classes | Java's built-in Cloneable interface is notoriously flawed |
| Allows adding or removing objects at runtime | Shallow 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.