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.
๐ฏ 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:
- He prepares one perfect sample card. Ravi's whole family checks it carefully โ names, date, venue, spelling of "Sunrise Garden, Jaipur", everything.
- Once everyone says "Haan, this is perfect!", he simply makes copies of that sample. The machine does the boring work.
- 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:
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
newand lots of setup code. The copying logic lives inside the object itself, usually in a method calledclone().
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 callcard.clone()and you will get the correct type of card back โ even if it was secretly aVipCard. 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:
- 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 asCard, and you do not know whether it is aWeddingCardor aBirthdayCard? Then you need uglyif-elsechecks for every type. - 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.
- 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?
- 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:
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:
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:
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:
- Create a common interface with one method:
clone(). Every class that wants to be copyable implements this interface. - 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.
- Implement
clone()to returnnew SameClass(this). Each class clones into its own type. So cloning aVipCardthrough a plainCardvariable still gives back a realVipCard. - 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.
- (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."
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:
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:
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 ClassicBeautiful! 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:
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 type | What shallow copy does | Is it safe? |
|---|---|---|
| Numbers, booleans | Copies 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 items | Shares 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 connection | Shares 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) # goldenTwo 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 copiesDate,Map,Set, typed arrays, and even objects with circular references โ things the oldJSON.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
| Situation | Use 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:
โ ๏ธ 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:
| Pattern | Main question it answers | How objects are born | Key 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 method | Uses 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 methods | Can be implemented using prototypes inside |
| Builder | "How do I assemble a complex object step by step?" | Step-by-step construction | Builder constructs fresh; Prototype duplicates ready-made |
| Singleton | "How do I ensure only ONE object exists?" | One instance, reused everywhere | The 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:
๐ฏ 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.
-
The school ID card machine. Create a
StudentIdCardclass withname,className,rollNumber, and a nestedSchoolInfoobject (school name, address, principal name). All 1,200 students share the same school info, but each card has a different name and roll number. Implementclone()so thatSchoolInfois shared (shallow โ it never changes per student) but the rest is copied. Then explain in one sentence why sharing was the right choice here. -
Catch the bug. Write a
TiffinBoxclass containing acompartmentsarray like roti, sabzi, pickle. Implement a wrong shallowclone(), clone the box, change the copy's pickle to "sweet", and print both boxes to show the bug. Then fixclone()with a proper deep copy of the array and show the corrected output. -
Mehendi design registry. Build a
DesignRegistrythat stores three prototype mehendi patterns ("bridal", "simple", "arabic"), each with a nestedPricingobject. Clients callregistry.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. -
College corner challenge. Build two card objects that point at each other (card A's field
partnerCardis 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 trickcopy.deepcopyuses. 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
Factory Method Pattern: Let the Branch Decide the Vehicle
Learn the Factory Method design pattern with a simple Indian tiffin service story, easy TypeScript and C# code, diagrams, real examples, and practice.
Abstract Factory Pattern: One Order, One Matching Thali
Understand the Abstract Factory design pattern through an Indian wedding catering story, with simple TypeScript and Python code, diagrams, and practice.
Builder Pattern: Stitch It Step by Step, Like a Master Tailor
Learn the Builder design pattern with an Indian tailor shop story, fluent TypeScript and C# code, the telescoping constructor problem, and practice tasks.
Singleton Pattern: One Principal for the Whole School
Understand the Singleton design pattern with a school principal story, simple TypeScript and C# code, thread safety, and why many seniors call it an anti-pattern.