Free Interactive Course · Design Patterns

Prototype Design Pattern

Copying a configured object beats rebuilding one — as long as you know how deep the copy goes.

Creational Patternsmediuma.k.a. Clone
ShareXLinkedIn
In one sentence

Create new objects by copying an existing configured instance, rather than constructing one from scratch.

01

The problem Prototype solves

The problem

Your reporting service builds a ReportTemplate: it loads a layout file, parses a stylesheet, compiles a set of formatting rules and resolves fonts. About 300 ms of work, and the result is a big object graph.

Every tenant needs that same template with two fields changed — their logo and their currency. So you run all 300 ms again, four hundred times a night, to produce four hundred objects that differ in two fields.

You can't cache one shared instance, because each tenant mutates their copy. And you can't easily rebuild "just the different bits", because the expensive part is the parsing, not the two fields.

02

How the Prototype pattern works

Build one fully-configured instance, then copy it whenever you need another:

  1. Build the expensive object once and keep it as the prototype.
  2. Give it a clone() that returns a new instance with the same state — no parsing, no I/O, just a copy.
  3. Callers clone and then change the handful of fields they care about.
The one decision that matters is copy depth. A shallow copy duplicates the object but shares everything it points at — so mutating clone.styles also changes the prototype's, and every other clone's. A deep copy duplicates the whole graph, which is correct but can cost as much as rebuilding. Most real prototypes are a deliberate mix: deep-copy the mutable parts, share the immutable ones.
ReportTemplatethe prototype+ clone() : ReportTemplatebuilt once · 300 msclone()copy · logo = acme.png0 mscopy · currency = EUR0 mscopy · locale = ja-JP0 msN objects,one setup
Participants. The Prototype declares clone(). A Concrete Prototype implements it. The Client asks a prototype to copy itself and never calls a constructor — which also means it never needs to know the concrete class.
03

See it: N objects, one expensive setup

Ask for several templates. With the pattern on you get a new object every time — that's the difference from Singleton — but only the first one pays for parsing. Turn it off and every request re-parses.

▶ Try it — clone vs. rebuild

An interactive heap: cloning a prototype produces a distinct new object on every call while paying the expensive setup cost only once, whereas constructing from scratch repeats the parsing work every time.

Compare the counters with Singleton's simulator. Singleton: 1 object, 1 setup. Prototype: N objects, 1 setup. Same saving, opposite guarantee — and that's exactly why they solve different problems.
04

Prototype pattern code examples

Cloning an expensively-built template, with copy depth chosen deliberately.

public final class ReportTemplate implements Cloneable {
    private final Layout layout;                 // immutable — safe to share
    private final Map<String, Style> styles;     // mutable  — must be copied
    private String logo;
    private String currency;

    public ReportTemplate(Path spec) {
        this.layout = LayoutParser.parse(spec);      // the expensive part
        this.styles = StyleSheet.compile(spec);
    }

    // Copy constructor rather than Object.clone(): explicit, works with final
    // fields, and doesn't drag in the Cloneable/CloneNotSupportedException mess.
    private ReportTemplate(ReportTemplate other) {
        this.layout   = other.layout;                       // shared: immutable
        this.styles   = new HashMap<>(other.styles);        // copied: mutable
        this.logo     = other.logo;
        this.currency = other.currency;
    }

    public ReportTemplate copy() { return new ReportTemplate(this); }

    public ReportTemplate withLogo(String logo) {
        ReportTemplate c = copy();
        c.logo = logo;
        return c;
    }
}

// ReportTemplate base = new ReportTemplate(Path.of("invoice.spec"));  // once
// ReportTemplate acme = base.withLogo("acme.png");                    // free
//
// Effective Java is blunt about java.lang.Cloneable: prefer a copy constructor
// or a static copy factory. Object.clone() bypasses constructors and interacts
// badly with final fields.
Read across the tabs and one theme dominates: every language gives you a cheap copy that looks complete and isn't. Go's cp := *t, C#'s with and MemberwiseClone(), JavaScript's spread, Python's copy.copy — all shallow, all compile, all share their nested state. The languages that make Prototype pleasant are the ones where you can mark the shareable parts immutable (readonly, shared_ptr<const T>) and then only copy what's left.
05

How to implement Prototype

  1. Confirm the copy is actually cheaper than the construction. If the object is a big mutable graph, a deep copy can cost more than re-parsing — measure before you commit.
  2. Classify every field: immutable (share it), mutable (copy it), identity (a database id or timestamp that must not be copied).
  3. Prefer a copy constructor or a clone() you wrote by hand over the language's magic clone. Explicit depth is the whole point.
  4. Return a new instance rather than mutating; combine with with-style overrides so callers can clone-and-change in one call.
  5. Make the prototype itself effectively immutable, or every clone inherits whichever mutation happened most recently.
  6. In a class hierarchy, make clone() virtual and return the concrete type, so cloning through a base reference produces the right subclass.
06

When to use Prototype — and when not to

Use it when construction is genuinely expensive (parsing, I/O, compilation) and most of the result is identical between instances; when you need many near-identical objects that each get mutated; when you must copy an object whose concrete class you don't know; or when the object's configuration comes from user actions and can't be re-derived from a constructor.

Where it goes wrong

The accidental shallow copy. The clone works in the test, ships, and then two tenants start seeing each other's styles because they share a mutable map. This is the defining bug of this pattern and it never shows up at compile time.

Deep copy that costs more than construction. copy.deepcopy() on a large graph can be slower than the parse you were avoiding — and it will happily copy the 40 MB immutable layout you meant to share.

Cloned identity. Copying an object that carries a database primary key, a UUID or a created-at timestamp produces two objects claiming to be the same row. Decide explicitly which fields must be reset.

Cycles and unclonable members. Object graphs with cycles need a visited-set, and members like open sockets, file handles or locks cannot meaningfully be copied at all.

You need…UseBecause
Exactly one shared instanceSingletonOne object, one setup. Prototype gives you N objects, one setup.
Many near-identical objects, each mutated separatelyPrototypeCopying skips the expensive construction; each copy is independent.
Many identical objects that are never mutatedFlyweightIf nobody mutates them, sharing beats copying outright.
A complicated object assembled from optional partsBuilderBuilder is about assembly; Prototype is about skipping it.
A new object whose class variesFactory MethodFactory picks a class to instantiate; Prototype avoids instantiation entirely.
07

Quick check

🧠 Quick check
A Template has a Map<String, Style> styles. Its clone() copies every field across directly. A tenant edits one style on their clone. What happens?

In the wild

JavaScriptObject.create(proto) — JavaScript's object model is prototypal, so this pattern is the language rather than a pattern in it.
Pythoncopy.copy() / copy.deepcopy() plus the __copy__ and __deepcopy__ hooks for per-class control.
JavaObject.clone() and Cloneable — the cautionary tale. Effective Java recommends copy constructors instead.
C#record types with with expressions, and MemberwiseClone() — both shallow by design.
Gomaps.Clone and slices.Clone (Go 1.21+), plus proto.Clone in Protocol Buffers.
C++The virtual-clone idiom, used throughout LLVM and Qt (QObject-style hierarchies) to copy through a base pointer.

Frequently asked questions

What is the difference between a shallow copy and a deep copy?
A shallow copy duplicates the object's own fields, so any field holding a reference — a list, map, array or object — ends up pointing at the same nested data as the original. A deep copy recursively duplicates the whole graph, so the two are fully independent. Shallow is fast and usually the default ({...obj}, with, MemberwiseClone, cp := *t); deep is correct but can be slower than simply rebuilding the object.
When should I use Prototype instead of a constructor?
When construction is genuinely expensive and most of its result is identical between instances — parsing a spec, compiling a template, loading reference data — or when the object's configuration was assembled at runtime and can't be re-derived from constructor arguments. If construction is cheap, a constructor is clearer and Prototype adds a maintenance burden with no payoff.
Why does Effective Java advise against Cloneable?
Object.clone() creates the copy without running any constructor, which breaks invariants that constructors are supposed to enforce and interacts badly with final fields. Cloneable also doesn't declare clone(), so it's a marker interface that changes the behaviour of a protected method — an unusual and confusing design. A copy constructor or static copy factory does the same job explicitly, works with final fields, and can return an interface type.
What's the difference between Prototype and Flyweight?
Both avoid repeating expensive work, in opposite ways. Prototype copies, giving you N independent objects that each cost almost nothing after the first. Flyweight shares, giving you one object referenced from many places. Choose Prototype when each copy will be mutated; Flyweight when the state is genuinely identical and read-only, since sharing then beats copying.

Finish the handbook, earn the certificate

All 23 Gang-of-Four patterns, each with a simulator you can click and code in seven languages. Free, no signup.

See all 23 patterns →
Found this course useful? Share it.
ShareXLinkedIn

Comments

0

Join the conversation. Sign in to leave a comment — we'd love to hear your thoughts.