Skip to main content
CleanCodeMastery

Prototype Pattern: Make Copies Like a Xerox Machine, Not From Scratch

Learn the Prototype design pattern with a simple wedding card story, easy TypeScript and Python examples, and a clear shallow vs deep copy demo.

24 min read Updated June 11, 2026beginner
design-patternscreational-patternsprototypeclonedeep-copyshallow-copytypescriptpython

๐ŸŽฏ The wedding card story from Sharma uncle's shop

Meet Ravi. He is in Class 10, and his cousin sister Riya didi is getting married in December. Riya didi will marry Arjun bhaiya, and the whole house is upside down with excitement. Ravi's father gives him one job: "Go to Sharma uncle's printing shop in the market. We need 500 wedding cards."

Ravi walks to the shop. Sharma uncle has run this small printing and xerox shop for twenty years. His son Montu helps after college. Now think about how Sharma uncle works. Does he design every single card from zero? Choose the paper 500 times? Draw the Ganesha border 500 times? Type the family names, the venue, the date, the dinner time โ€” 500 times?

Of course not! That would take months. And somewhere in card number 387, he would surely spell "Arjun" as "Arjum".

Instead, Sharma uncle does something very smart:

  1. He prepares one perfect sample card. Ravi's whole family checks it carefully โ€” names, date, venue, spelling of "Sunrise Garden, Jaipur", everything.
  2. Once everyone says "Haan, this is perfect!", he simply makes copies of that sample. The machine does the boring work.
  3. For a few special cards โ€” for the groom's family, for VIP guests โ€” Montu takes a copy and changes one or two small things. Maybe a golden border. Maybe a personal name written on top.

This is exactly the Prototype pattern. You do not build each object from scratch. You keep one perfectly prepared object โ€” the prototype, like the sample card โ€” and you make copies of it whenever you need a new one. If a copy needs small changes, you change only those small parts.

The same idea sits in the xerox machine in the corner of the shop. When Ravi needs ten copies of a filled school form, he does not write the form ten times by hand. He gives one filled form to the machine, and it gives faithful copies. The machine does not need to understand the form. It just copies it.

Here is Ravi's full morning, as a journey:

Figure 1: Ravi's trip to the wedding card shop

Keep this shop in your mind. Ravi, Sharma uncle, Montu, and the sample card will walk with us through the whole article โ€” and yes, we will write the shop in code.

๐Ÿ’ก What is the Prototype pattern?

The Prototype pattern is a creational design pattern. "Creational" means it is about how objects are created.

Here is the plain definition:

Prototype lets you create new objects by copying an existing object (called the prototype), instead of building a new object from scratch with new and lots of setup code. The copying logic lives inside the object itself, usually in a method called clone().

Three small ideas sit inside this definition:

  • The object copies itself. You do not stand outside and copy field by field. You politely ask the object, "Please give me a copy of yourself," and it does the work. The object knows its own private details, so its copy is complete. Sharma uncle knows his own card best โ€” Ravi does not need to know what paper or ink was used.
  • The caller does not need to know the exact class. If you hold something as a general Card, you can call card.clone() and you will get the correct type of card back โ€” even if it was secretly a VipCard. Ravi just says "copy this", he never asks "is this an offset print or digital print?"
  • Copies can be adjusted after cloning. Clone first, then change the one or two fields that differ. Like Montu's golden border.
๐Ÿ’ก

One-line memory trick: "Why build again when you can xerox?" Prototype = keep one perfect sample, then clone it.

The pattern is also called Clone, for the obvious reason. It is one of the original 23 patterns from the famous Design Patterns book by the Gang of Four (Gamma, Helm, Johnson and Vlissides, 1994).

College corner: in GoF terms, Prototype trades class-based creation for object-based creation. Your code depends on an interface with clone(), not on concrete constructors, so it follows the Dependency Inversion Principle for object creation. It also reduces subclass explosion: instead of one subclass per configuration, you keep one configured instance per configuration.

โš ๏ธ The problem it solves

Let us feel the pain first. Suppose there is no Prototype pattern, and we need a second card exactly like the first one. The "obvious" way is to make a new object and copy every field by hand:

// The painful, copy-by-hand way โ€” please don't do this!
const original = new WeddingCard("Riya weds Arjun", "Jaipur", "12 Dec 2026");
 
const copy = new WeddingCard(
  original.coupleNames,  // copy field 1
  original.venue,        // copy field 2
  original.date          // copy field 3
);
// ...and field 4, 5, 6... hope we did not forget any!

This looks okay for three fields. But real objects are not this small. Here is why this approach breaks down:

  1. You must know the exact class. To write new WeddingCard(...), your code is glued to that specific class. What if you only hold a variable typed as Card, and you do not know whether it is a WeddingCard or a BirthdayCard? Then you need ugly if-else checks for every type.
  2. Private fields are invisible. Many objects keep secret internal state in private fields. Code outside the object simply cannot read them. So your hand-made copy will always be incomplete โ€” like photocopying only the front page of a two-page form.
  3. Setup may be expensive. Maybe the original object was built using a database call, a network request, or a heavy calculation. Building a second one from scratch repeats all that cost. Why cook the full meal again when the food is already on the table?
  4. It is easy to forget a field. Tomorrow someone adds field number 7 to the class but forgets to update your copying code. Now your copies are silently wrong. These bugs are very hard to find.

Where does the time actually go when you build a rich object from scratch? Roughly like this:

Figure 2: Where time goes when building one rich object from scratch

Cloning skips almost all of that, because the loaded data and computed fields are already sitting inside the sample. Here is the decision as a picture:

Figure 3: Building from scratch vs cloning a prototype

The Prototype pattern says: stop copying from outside. Put a clone() method inside the class. The object has full access to all its own fields โ€” even private ones โ€” so it can produce a perfect copy. The caller just says original.clone() and is done.

And the savings are not small. If preparing one card from scratch costs heavy setup, cloning costs only a memory copy:

Figure 4: Rough cost of producing 100 similar objects

The exact numbers depend on your program, but the shape of this chart is very common: build once, clone many, save a lot.

๐Ÿ› ๏ธ How it works, step by step

Here is the standard recipe. It is short, and you can follow it in any language:

  1. Create a common interface with one method: clone(). Every class that wants to be copyable implements this interface.
  2. Write a copy constructor (or copy logic) in each class. This constructor takes another object of the same class and copies all its fields. In child classes, first call the parent's copy logic so inherited fields are copied too.
  3. Implement clone() to return new SameClass(this). Each class clones into its own type. So cloning a VipCard through a plain Card variable still gives back a real VipCard.
  4. Decide shallow or deep for every reference field. If a field points to another object, you must choose: share that object (shallow) or copy it too (deep)? This is the most important decision in the whole pattern. We will see it clearly in code below.
  5. (Optional) Build a prototype registry. This is a small catalogue โ€” like the shelf of sample cards behind Sharma uncle's counter. You register ready-made prototypes by name ("simple-card", "vip-card"), and clients ask the registry: "Give me a fresh copy of vip-card."
Figure 5: Class structure of the Prototype pattern

Notice the registry holds samples and always returns clones. The samples on the shelf never leave the shop โ€” just like Sharma uncle never gives away his master card.

The conversation between client and prototype is tiny โ€” that is the beauty:

Figure 6: One clone request, from order to copy

A sample card also has a small life story of its own. It gets prepared once, gets approved, and then spends its whole life being cloned:

Figure 7: Lifecycle of a prototype object

Read the loop in Figure 7 carefully: every clone() call passes through Cloning and produces a CopyReady object, but the sample itself always returns safely to SampleReady. If a clone ever damages the sample, your pattern is broken โ€” we will see exactly how that accident happens in the shallow copy section.

๐Ÿงช Real-life code example: the wedding card shop in TypeScript

Let us build the full shop. Read the comments slowly โ€” they explain each step.

// Step 1: the common interface. Anything copyable has clone().
interface CardPrototype {
  clone(): CardPrototype;
}
 
// A small nested object: the visual design of the card.
// This will help us understand shallow vs deep copy.
class Design {
  constructor(
    public borderColor: string,
    public font: string
  ) {}
 
  // The design knows how to copy itself too.
  clone(): Design {
    return new Design(this.borderColor, this.font);
  }
}
 
// Step 2 + 3: the concrete prototype with a copy constructor.
class WeddingCard implements CardPrototype {
  coupleNames: string;
  venue: string;
  date: string;
  design: Design; // reference field โ€” careful here!
 
  constructor(coupleNames: string, venue: string, date: string, design: Design) {
    this.coupleNames = coupleNames;
    this.venue = venue;
    this.date = date;
    this.design = design;
  }
 
  // clone() builds a new WeddingCard from "this".
  clone(): WeddingCard {
    return new WeddingCard(
      this.coupleNames,      // string: copied by value, safe
      this.venue,            // string: safe
      this.date,             // string: safe
      this.design.clone()    // DEEP copy of the nested design!
    );
  }
 
  describe(): string {
    return `${this.coupleNames} | ${this.venue} | ${this.date} ` +
           `| border: ${this.design.borderColor}, font: ${this.design.font}`;
  }
}

Now the registry โ€” the shelf of sample cards behind the counter:

// Step 5: the prototype registry (the sample shelf).
class CardRegistry {
  private samples = new Map<string, CardPrototype>();
 
  register(name: string, sample: CardPrototype): void {
    this.samples.set(name, sample);
  }
 
  // Never hand out the sample itself โ€” always a fresh clone!
  get(name: string): CardPrototype {
    const sample = this.samples.get(name);
    if (!sample) throw new Error(`No sample named "${name}"`);
    return sample.clone();
  }
}
 
// ---------- Client code: a day in the shop ----------
 
// Sharma uncle prepares ONE perfect sample.
const sample = new WeddingCard(
  "Riya weds Arjun",
  "Sunrise Garden, Jaipur",
  "12 Dec 2026",
  new Design("maroon", "Devanagari Classic")
);
 
const registry = new CardRegistry();
registry.register("riya-arjun", sample);
 
// Ravi's family orders cards. Each order = one clone.
const card1 = registry.get("riya-arjun") as WeddingCard;
const card2 = registry.get("riya-arjun") as WeddingCard;
 
// A VIP guest wants a golden border. Montu changes ONLY the copy.
card2.design.borderColor = "golden";
 
console.log("Card 1:", card1.describe());
console.log("Card 2:", card2.describe());
console.log("Sample:", sample.describe());

Expected output:

Card 1: Riya weds Arjun | Sunrise Garden, Jaipur | 12 Dec 2026 | border: maroon, font: Devanagari Classic
Card 2: Riya weds Arjun | Sunrise Garden, Jaipur | 12 Dec 2026 | border: golden, font: Devanagari Classic
Sample: Riya weds Arjun | Sunrise Garden, Jaipur | 12 Dec 2026 | border: maroon, font: Devanagari Classic

Beautiful! Card 2 got a golden border, but Card 1 and the master sample stayed maroon. Each copy is independent. Sharma uncle's shelf is safe.

โš ๏ธ The shallow copy trap โ€” a live demo

Now the most important section of this whole article. What if clone() had been lazy and shared the design instead of copying it? This is called a shallow copy:

// A WRONG clone โ€” shallow copy of the design.
clone(): WeddingCard {
  return new WeddingCard(
    this.coupleNames,
    this.venue,
    this.date,
    this.design        // โš ๏ธ same Design object is SHARED!
  );
}

With this version, run the same client code:

Card 1: ... | border: golden, font: Devanagari Classic   โ† changed!
Card 2: ... | border: golden, font: Devanagari Classic
Sample: ... | border: golden, font: Devanagari Classic   โ† even the master changed!

Disaster! Montu changed Card 2's border, but Card 1 and the master sample turned golden too. Imagine Sharma uncle's face: one customer asked for golden, and now every future card in the shop prints golden. Why did this happen? Because all three cards were pointing to the same single Design object in memory. We photocopied the card but stapled all copies to one shared design sheet.

This picture shows exactly what sits in memory in both cases:

Figure 8: Shallow copy shares the nested object; deep copy duplicates it

Here is the rule, in simple words:

  • Shallow copy = copy the top box only. Nested objects inside are still shared. Fast, but risky for mutable nested objects.
  • Deep copy = copy the box and everything inside it, all the way down. The clone becomes fully independent.
Field typeWhat shallow copy doesIs it safe?
Numbers, booleansCopies the valueโœ… Always safe
Strings (in JS/TS, Python, C#, Java)Copies the reference, but strings cannot changeโœ… Safe (immutable)
Nested mutable object (like Design)Shares one object between original and cloneโŒ Risky โ€” changes leak both ways
Arrays and lists of mutable itemsShares the same listโŒ Risky โ€” push into one, both grow
Shared read-only object (like a global config)Shares itโœ… Usually fine, sharing is intended
Open file handle, socket, DB connectionShares the live resourceโš ๏ธ Sharing may be the only sane option โ€” duplicating a live socket is meaningless

A good clone() is usually a mix: copy simple values directly, share immutable things, and deep-copy any mutable object that truly belongs to this object. Decide field by field, and write a small comment for each decision.

College corner โ€” copy semantics, properly: what we casually call "deep copy" is really a question of object graph traversal. A full deep copy is a recursive walk over the reachable graph, and three hard cases appear. (1) Cycles: if A points to B and B points back to A, naive recursion never ends; real implementations like Python's copy.deepcopy keep a memo dictionary mapping already-copied ids to their copies. (2) Aliasing: if two fields point to the same object, a correct deep copy must preserve that sharing inside the clone (both copied fields point to one copied object), not create two copies. The memo solves this too. (3) Identity vs equality: after a correct deep copy, clone == original may be true (equal values) while clone is original is false (different identity) โ€” and this must hold recursively. C++ formalises all this with copy constructors and the Rule of Three/Five; Java's default Object.clone() is shallow and you must override it carefully; JavaScript's structuredClone handles cycles but refuses to copy functions and class methods (you get a plain object back). Know your language's default before trusting it.

๐Ÿงช The same idea in Python

Python makes Prototype very pleasant, because the standard library ships a ready-made copy module with both kinds of copy.

import copy
 
class Design:
    def __init__(self, border_color: str, font: str):
        self.border_color = border_color
        self.font = font
 
class WeddingCard:
    def __init__(self, couple, venue, date, design: Design):
        self.couple = couple
        self.venue = venue
        self.date = date
        self.design = design
 
    def clone(self) -> "WeddingCard":
        # deepcopy walks the whole object and copies
        # every nested mutable object too.
        return copy.deepcopy(self)
 
sample = WeddingCard(
    "Riya weds Arjun",
    "Sunrise Garden, Jaipur",
    "12 Dec 2026",
    Design("maroon", "Devanagari Classic"),
)
 
vip_card = sample.clone()
vip_card.design.border_color = "golden"
 
print(sample.design.border_color)    # maroon  โ† master is safe!
print(vip_card.design.border_color)  # golden

Two functions to remember from Python's copy module:

  • copy.copy(x) โ†’ shallow copy (top level only).
  • copy.deepcopy(x) โ†’ deep copy (everything, recursively; it even handles objects that point back to themselves, thanks to the memo dictionary we met in the College corner).

You can also customise the copying by writing __copy__ and __deepcopy__ methods in your class โ€” that is Python's official hook for the Prototype pattern. Use it when some field must be shared (a logger, a cache) even during a deep copy.

๐Ÿ’ก Where you see it in real software

This pattern is not just a textbook story. You meet it every day:

  • JavaScript's structuredClone(). Modern browsers and Node.js (17+) have a built-in deep copy function. It correctly copies Date, Map, Set, typed arrays, and even objects with circular references โ€” things the old JSON.parse(JSON.stringify(x)) trick destroys. When a web app keeps an undo history by snapshotting state, it is doing Prototype.
  • Python's copy.deepcopy(). Used everywhere from data science (copying a model configuration before tweaking it) to games. The python-patterns repository has a clean Prototype example with a registry/dispatcher.
  • Game development โ€” spawning objects. A game does not build every enemy from scratch. It keeps one fully prepared enemy template (model, sounds, default health) and clones it each time a new enemy appears. Unity's Instantiate(prefab) is exactly this idea: the prefab is the prototype, and the game might clone it sixty times a second.
  • Document and graphics editors. "Duplicate slide" in presentation software, "duplicate layer" in Photoshop, copy-paste of a drawn shape โ€” every duplicate is a clone of a configured object. Notice how a duplicated slide is independent: editing it never touches the original. Somebody implemented a careful deep copy for you.
  • Java's Cloneable / copy constructors. Java has had cloning in the language since the beginning. The iluwatar/java-design-patterns repository shows a tidy modern Prototype example you can read in ten minutes.
  • JS object system itself. JavaScript is literally a prototype-based language: objects inherit from other objects via the prototype chain, and Object.create(proto) makes a new object based on an existing one. The naming is not a coincidence โ€” the language design borrowed the same idea.

๐Ÿ“Š When to use it and when not to

SituationUse Prototype?
Creating the object is expensive (DB call, network, heavy maths) and you need many similar onesโœ… Yes โ€” clone the prepared one
You need many objects that differ only slightly from a baselineโœ… Yes โ€” clone, then tweak
Your code holds objects only by interface and must copy them without knowing concrete classesโœ… Yes โ€” clone() keeps the real type
You are tempted to create many subclasses only to store different configurationsโœ… Yes โ€” keep configured prototypes instead
The object is tiny and cheap, e.g. a small point with x and yโŒ No โ€” plain new is simpler and clearer
The object holds things that must NOT be duplicated (open file handles, sockets, DB connections)โŒ No โ€” cloning a live connection makes no sense
The object graph has tangled circular references and you cannot control deep copy carefullyโŒ Be careful โ€” deep copy of cycles needs special handling
Objects are immutable anywayโŒ Usually no โ€” immutable objects can be safely shared, no copy needed

If you like to decide with a picture, place your case on this map. Prototype shines in the top-right corner โ€” costly objects, needed in many copies:

Figure 9: Where Prototype pays off

โš ๏ธ Common mistakes students make

โš ๏ธ

Mistake 1: Shallow copying mutable nested objects. This is the number one Prototype bug in the whole world. You clone the card, change the copy's design, and mysteriously the original changes too โ€” Sharma uncle's golden disaster. Whenever a field holds a mutable object, ask loudly: "Share or copy?" โ€” and write your answer in the code.

โš ๏ธ

Mistake 2: Forgetting parent fields in subclasses. If VipCard extends WeddingCard, the VipCard copy constructor must first call the parent's copy logic (super(source)), otherwise the couple's names and venue silently stay empty in the clone. A wedding card without the couple's names is just expensive paper.

โš ๏ธ

Mistake 3: Handing out the prototype itself from the registry. The registry must return sample.clone(), never sample. If you return the master sample, the first customer who scribbles on it ruins it for everyone โ€” just like giving away the shop's only master card.

โš ๏ธ

Mistake 4: Using JSON.parse(JSON.stringify(obj)) for deep copy in JavaScript. It drops functions, converts Date to a plain string, loses Map and Set, and crashes on circular references. Use structuredClone() for data, or a proper clone() method when behaviour also matters.

๐Ÿ“Š Compare with cousins

Prototype is one of five classic creational patterns. Here is how it stands beside its cousins:

PatternMain question it answersHow objects are bornKey difference from Prototype
Prototype"How do I get another one like this?"Copy an existing objectโ€”
Factory Method"Which subclass decides what to create?"Subclass overrides a creation methodUses inheritance + new; Prototype uses copying, no subclasses needed
Abstract Factory"How do I create whole families of related objects?"A factory object with many create methodsCan be implemented using prototypes inside
Builder"How do I assemble a complex object step by step?"Step-by-step constructionBuilder constructs fresh; Prototype duplicates ready-made
Singleton"How do I ensure only ONE object exists?"One instance, reused everywhereThe exact opposite goal: Prototype multiplies objects, Singleton forbids multiplying!

A nice fact from the Gang of Four book: designs often start with Factory Method (simple, but needs subclasses) and grow toward Abstract Factory or Prototype as more flexibility is needed.

To revise the whole landscape in one glance, here is the pattern as a mind map:

Figure 10: Prototype at a glance

๐ŸŽฏ Quick revision box

+-------------------------------------------------------------+
|                  PROTOTYPE PATTERN โ€” REVISION               |
+-------------------------------------------------------------+
| Idea       : Make new objects by COPYING a sample object    |
| Nickname   : Clone ("the xerox pattern")                    |
| Key method : clone() โ€” lives INSIDE the object              |
| Story      : One perfect wedding card -> 500 copies         |
|                                                             |
| Steps      : 1. Interface with clone()                      |
|              2. Copy constructor in each class              |
|              3. clone() returns new SameClass(this)         |
|              4. Decide SHALLOW vs DEEP per field            |
|              5. Optional: registry of named samples         |
|                                                             |
| Shallow    : top level only, nested objects SHARED  โš ๏ธ      |
| Deep       : nested objects copied too, fully independent โœ… |
|                                                             |
| Use when   : creation is costly; many similar objects;      |
|              cannot depend on concrete classes              |
| Avoid when : tiny cheap objects; live connections/handles   |
| Real world : structuredClone(), copy.deepcopy(),            |
|              game prefabs, "duplicate slide"                |
+-------------------------------------------------------------+

๐Ÿงช Practice exercise

Try these three tasks on your own. They go from easy to slightly tricky โ€” Ravi-level to Montu-level.

  1. The school ID card machine. Create a StudentIdCard class with name, className, rollNumber, and a nested SchoolInfo object (school name, address, principal name). All 1,200 students share the same school info, but each card has a different name and roll number. Implement clone() so that SchoolInfo is shared (shallow โ€” it never changes per student) but the rest is copied. Then explain in one sentence why sharing was the right choice here.

  2. Catch the bug. Write a TiffinBox class containing a compartments array like roti, sabzi, pickle. Implement a wrong shallow clone(), clone the box, change the copy's pickle to "sweet", and print both boxes to show the bug. Then fix clone() with a proper deep copy of the array and show the corrected output.

  3. Mehendi design registry. Build a DesignRegistry that stores three prototype mehendi patterns ("bridal", "simple", "arabic"), each with a nested Pricing object. Clients call registry.get("bridal") and must always receive an independent clone. Prove it with a test: take two clones of "bridal", raise the price of one, and assert the other clone and the registry's master are unchanged.

  4. College corner challenge. Build two card objects that point at each other (card A's field partnerCard is B, and B's is A). Write a naive recursive deep copy and watch it recurse forever. Then fix it with a memo map keyed by object identity, the same trick copy.deepcopy uses. Write down why the memo also preserves shared references correctly.

If you can finish task 3 without looking back at this article, you have truly understood Prototype. Well done โ€” go take a small break. Sharma uncle would offer you chai.

Frequently asked questions

What is the Prototype pattern in one line?
Prototype is a creational pattern where you create new objects by copying a ready-made object (the prototype) instead of building everything from scratch with new.
What is the difference between shallow copy and deep copy?
A shallow copy copies only the top level, so nested objects are still shared between original and copy. A deep copy also copies the nested objects, so the two objects become fully independent.
When should I use Prototype instead of a constructor?
Use Prototype when making an object is costly or complicated, when you need many objects that differ only a little, or when your code should not depend on concrete class names.
Is JavaScript's structuredClone() the Prototype pattern?
structuredClone() is a built-in deep copy tool. It does the copying work for you, which is the heart of the Prototype idea, but the full pattern also includes a clone() method on the object itself and sometimes a registry of prototypes.
What is a prototype registry?
A prototype registry is a small catalogue that stores ready-made prototype objects by name. When you ask for a name, it hands you a fresh clone, so it works like a simple factory without new classes.

Further reading

Related Lessons