1.5 Prototype
The Prototype pattern creates objects with a fundamentally different idea than a factory: instead of constructing from scratch, it copies an existing object. When an object is expensive to initialize (reading config, querying a database, heavy computation) but you need many "nearly identical" copies, cloning is often far faster and simpler than rebuilding.
It also has a hidden benefit: when you hold an instance but don't know its concrete class, calling its clone() still yields a new object of the same type.
But Prototype has a trap almost everyone hits: shallow vs deep copy. The profile copy lab below does one thing: copy a profile, edit the copy, and see whether the original gets dragged along.
Why a shallow copy "leaks"
A shallow copy duplicates only the outermost fields of an object. If a field is a reference (pointing to another mutable object), then the clone and the original share the same inner object:
class Doc implements Cloneable {
Address address; // reference type
// Shallow copy: address is copied by reference, still shared
public Doc clone() throws CloneNotSupportedException {
return (Doc) super.clone();
}
// Deep copy: duplicate the inner object too, fully isolated
public Doc deepClone() throws CloneNotSupportedException {
Doc copy = (Doc) super.clone();
copy.address = this.address.clone();
return copy;
}
}Edit the clone's address.city and the original's city changes too — that is the shallow copy "leak." Flip the "deep copy" switch in the lab above to see the two objects become fully independent.
Cloneable awkward and instead use a copy constructor, serialize/deserialize, or library helpers (like BeanUtils or a JSON round-trip) to deep-copy. The spirit of the pattern is unchanged: create new objects by copying an existing instance.Prototype registry: cloning in bulk
Prototype often pairs with a "registry": register several pre-configured prototypes, then fetch by key and clone on demand. This avoids repeating heavy initialization while keeping every copy independent.
In the prototype registry battlefield below, clicking an enemy type is registry.get(kind).clone(), spawning an independent instance; damage one clone and the others are untouched.
When to use Prototype
- Object creation is expensive (heavy init) but you need many similar copies.
- You want to copy by an object's current state at runtime, not reset to the initial configuration.
- You want to hide concrete classes and copy whatever instance you hold via
clone().
Conversely, if objects are lightweight and cheap to build, a plain new is clearer — and remember the extra mental load of deep vs shallow copy.