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 copyJava 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:
Cloneableis a broken design in Java (protectedclone(), 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); }.
Related Concepts
| Concept | Relationship |
|---|---|
| Factory-Method-Pattern | Factory Method creates via subclass; Prototype creates via cloning an existing instance |
| Abstract-Factory-Pattern | Factories can use Prototype to clone products rather than construct them |
| Builder-Pattern | Builder constructs from scratch; Prototype copies from an existing instance |
| Singleton-Pattern | Singleton 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