Prototype Pattern

Prototype Pattern

Specify the kinds of objects to create using a prototypical instance, and create new objects by copying (cloning) this prototype.

Intent

Instead of constructing a new object from scratch, the Prototype pattern clones an existing instance. This is useful when construction is expensive (e.g., loading from a database or computing initial state) and a copy is cheaper. It also allows object creation to be configured at runtime by registering prototype instances.

When NOT to Use

  • Object construction is cheap — cloning adds complexity for no benefit
  • The object has no mutable internal state — use a Flyweight or constant instead
  • Deep clone semantics are complex and error-prone for the class hierarchy — explicit construction is safer
  • The object has circular references — deep cloning becomes recursive and fragile

When to Use

  • Object creation is expensive and a copy is cheaper than reconstruction
  • Classes to instantiate are specified at runtime (e.g., from a registry or configuration)
  • You want to avoid a subclass hierarchy that mirrors the product hierarchy

How It Works

Participants: Prototype (interface declaring clone()), ConcretePrototype (implements clone()), Client (calls prototype.clone() instead of new ConcretePrototype()).

Critical concern — shallow vs deep clone: A shallow copy duplicates the object's primitive fields but shares references to nested objects. If nested objects are mutable, mutations via the clone affect the original. Always document which semantics your clone() implements.

Class Diagram

classDiagram
    class Prototype {
        <<interface>>
        +clone()* Prototype
    }
    class ConcretePrototype1 {
        -field1
        +clone() Prototype
    }
    class ConcretePrototype2 {
        -field1
        -nestedObject
        +clone() Prototype
    }
    class Client {
        +operation(prototype)
    }
    Prototype <|.. ConcretePrototype1
    Prototype <|.. ConcretePrototype2
    Client --> Prototype : clone()

    note for ConcretePrototype2 "Deep clone: must also clone nestedObject\nShallow clone: shares nestedObject reference"

TypeScript Example

// Prototype — TypeScript (shallow vs deep clone)
interface Cloneable<T> {
  clone(): T;
}
 
class Address {
  constructor(public city: string) {}
}
 
class User implements Cloneable<User> {
  constructor(public name: string, public address: Address) {}
 
  shallowClone(): User {
    return Object.assign(Object.create(Object.getPrototypeOf(this)), this);
    // WARNING: address is still shared — mutating clone.address mutates the original
  }
 
  clone(): User {
    return new User(this.name, new Address(this.address.city)); // deep copy of mutable field
  }
}
 
const original = new User("Alice", new Address("London"));
const shallow = original.shallowClone(); // shallow.address === original.address (shared reference)
const deep = original.clone();           // deep.address is an independent copy

Java Example

// Prototype — Java (Cloneable + copy constructor)
public class Address {
    public String city;
    public Address(String city) { this.city = city; }
}
 
public class User implements Cloneable {
    public String name;
    public Address address;
 
    public User(String name, Address address) {
        this.name = name;
        this.address = address;
    }
 
    @Override
    public User clone() throws CloneNotSupportedException {
        User copy = (User) super.clone(); // shallow — address is still shared
        copy.address = new Address(this.address.city); // explicit deep copy of mutable field
        return copy;
    }
}

Java note: Cloneable is a broken design in Java (protected clone(), throws checked exception, shallow by default — Bloch, Effective Java Item 13). For new code, prefer a copy constructor: public User(User source) { this.name = source.name; this.address = new Address(source.address.city); }.

ConceptRelationship
Factory-Method-PatternFactory Method creates via subclass; Prototype creates via cloning an existing instance
Abstract-Factory-PatternFactories can use Prototype to clone products rather than construct them
Builder-PatternBuilder constructs from scratch; Prototype copies from an existing instance
Singleton-PatternSingleton prevents multiple instances; Prototype enables multiple independent copies
Value-Object (created Phase 10)Value Objects use Prototype's copy semantics — immutable equality by value, copy on mutation

Sources

  • Gamma et al., Design Patterns, 1994, pp. 117–126 — authoritative definition
  • Bloch, Effective Java 3rd ed., Item 13 — Java Cloneable design flaws and copy constructor recommendation
  • refactoring.guru/design-patterns/prototype — modern framing