Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

Prototype
LLD

Prototype

Learn how to create new objects by cloning existing instances instead of constructing them from scratch.

Prototype: Shallow vs Deep Copy

The Problem It Solves

A game spawns thousands of enemies sharing 95% configuration (model, stats, AI tree) differing in position. Reconstructing each from scratch means re-parsing configs, rebuilding nested structures — expensive repeated work for nearly-identical objects. Prototype copies an existing, fully-configured instance instead of building from blueprints. The entire difficulty of the pattern lives in one question: what exactly does “copy” mean when fields point to other objects?

Shallow vs Deep

 ORIGINAL                     SHALLOW copy.clone()          DEEP copy

 enemy ┌─ pos ────────┐      clone ┌─ pos ──┐              clone ┌─ pos(new)┐
       │              │             ▲                │            ▲
       ├─ weapon ◄────┼─────────────┼────────────────┼────────────┤
       │    (shared!) │      clone.weapon IS original.weapon   both have OWN weapon
       └─ tags[...] ──┼─────────────┘
                      │        mutating shared.weapon via EITHER
                      ▼        reference corrupts BOTH objects
  • Shallow: top-level fields duplicated; referenced objects shared. Cheap; correct only for immutable or intentionally-shared parts.
  • Deep: entire object graph recursively copied. Safe independence; costs time proportional to graph size.
class Enemy {
    private final Position pos;                 // mutable value object
    private final Weapon weapon;                // mutable aggregate
    private final List<String> tags;

    // DEEP COPY CONSTRUCTOR — the recommended technique
    Enemy(Enemy other) {
        this.pos    = new Position(other.pos);
        this.weapon = new Weapon(other.weapon);
        this.tags   = List.copyOf(other.tags);  // defensive immutable copy
    }
    Enemy deepCopy() { return new Enemy(this); }
}

Why Copy Constructors Beat clone()

Cloneable is a famously broken design (Effective Java Item 13):

IssueConsequence
Object.clone() is native shallow copyDeep needs risky override surgery
Cloneable has no methodsPure marker; clone() sits on Object anyway
Final fieldsclone() assigns them natively — can’t re-set deep-copied values in the subclass
Contracts (“no constructor called”)Invariants enforced in constructors get bypassed
ExceptionsCloneNotSupportedException checked noise

Copy constructors/deepCopy() methods are ordinary code — type-safe, final-field-compatible, invariant-respecting.

Other Production Techniques

  • Serialization round-trip: serialize to bytes, deserialize → deep copy for free; slow but handles arbitrary graphs (used in snapshotting state stores).
  • Immutable prototypes: if the prototype is fully immutable, shallow “copies” are just shared references — the cheapest possible cloning; combine with copy-on-write for mutation.
  • Registry pattern: prototypes.get("orc").deepCopy() — config-loaded archetypes cloned per spawn.

Real-World Sightings

  • Game engines: entity templates cloned per spawn.
  • Object.clone() on arrays: shallow but fine — elements are primitives/immutable in most uses.
  • Spring’s prototype bean scope: container hands a fresh instance per request — same idea at framework scale (though it constructs rather than copies).

Cost Model (illustrative)

Copying a 1 KB object graph with 10 nested mutables ≈ microseconds vs milliseconds to rebuild from config parse + network fetch — the win evaporates if graphs are huge; then build-once-share-immutables beats deep-copying.

Interview Framing

  • The scoring moment is the diagram question: “your clone shares weapon — what breaks?” Answer with the aliasing-mutation story.
  • Recommending copy constructors over Cloneable with reasons is the experienced signal.

My Private Notes

Notes are auto-saved locally to this device.