Prototype
Prototype creates new objects by copying an existing instance rather than instantiating a class directly, so an object can be duplicated without the copying code knowing its concrete type.
Problem
A drawing app builds shapes at runtime - a `Circle` placed and dragged into position, recolored, resized. Producing a near-identical copy by calling `new Circle(x, y, color, radius)` means the caller has to know every constructor argument and re-read the original's current field values one by one, which gets brittle the moment a subclass like `Rectangle` adds its own fields the caller doesn't know about.
It gets worse for code that only holds the shape through a common `Shape` reference: it has no way to call `new Circle(...)` or `new Rectangle(...)` on it at all - it doesn't know, and shouldn't need to know, which concrete subclass it's holding.
Solution
Give every shape a `clone()` method it implements on itself, declared through the shared `Shape` interface. Because the method lives inside the class being copied, `Circle` and `Rectangle` each know exactly which fields they carry and copy themselves accordingly, without the caller enumerating constructor arguments by hand.
Calling code never constructs anything with `new` - it just calls `.clone()` on whatever `Shape` it's holding, and gets back an independent copy of the same concrete type (a `Circle` stays a `Circle`), with no need to know what that type is.
When to Use
- Reach for it when code holds objects only through a common interface and has to duplicate them without ever knowing their concrete class.
- Reach for it when building a new instance from scratch is measurably more expensive than copying an already-configured one.
- Reach for it when you're maintaining a growing set of subclasses that differ only in preset field values - a library of cloneable prototypes replaces that hierarchy.
Real-World Examples
- **`structuredClone()` in browsers and Node.js** - a built-in deep-clone operation that duplicates arbitrary objects, including nested structures, without external libraries.
- **Java's `Object.clone()` and `Cloneable` interface** - a built-in language-level protocol for objects to define their own shallow or deep copy behavior.
- **Unity/Unreal prefab and blueprint instantiation** - spawning a game object from a preconfigured template copies a fully set-up prototype rather than rebuilding its components from scratch each time.
Structure
A shared interface guarantees that any conforming object can clone itself on request. Each concrete class implements that cloning logic internally, where it has full access to its own private state and can decide what needs a deep copy versus a shared reference. Calling code only ever invokes the interface method, staying oblivious to which concrete class is actually being duplicated.
Participants
Interface guaranteeing a self-cloning capability, typically exposed as a single clone() method.
In the diagram: the two Prototype boxes (original and clone).
Implements clone() with direct access to its own private fields, deciding what to deep-copy so the clone doesn't secretly share mutable state with the original.
In the diagram: Circle.
Requests a copy through the Prototype interface alone, never referencing or depending on the concrete class being duplicated.
In the diagram: Client.
class Shape {
constructor({ x = 0, y = 0, color = 'black' } = {}) {
this.x = x;
this.y = y;
this.color = color;
}
clone() {
return new this.constructor({ ...this });
}
}
class Circle extends Shape {
constructor({ x, y, color, radius = 0 } = {}) {
super({ x, y, color });
this.radius = radius;
}
}
class Rectangle extends Shape {
constructor({ x, y, color, width = 0, height = 0 } = {}) {
super({ x, y, color });
this.width = width;
this.height = height;
}
}
const original = new Circle({ x: 10, y: 20, color: 'red', radius: 15 });
const copy = original.clone();
copy.color = 'blue';
console.log(original.color);
console.log(copy.color);
console.log(copy instanceof Circle);
Step 1 of 6
Shape holds the fields every clone will copy
The constructor sets `x`, `y`, and `color` from a destructured options object - these are the fields `clone()` will need to duplicate onto the new object.
Step 2 of 6
clone() copies the current instance instead of asking the caller to rebuild it
`new this.constructor({ ...this })` spreads all of the current object's own fields into a new instance of the same concrete class - the caller never has to know or repeat which fields exist.
Step 3 of 6
Circle extends Shape with its own field, clone() is inherited unchanged
`Circle` only adds `radius` in its constructor - it doesn't redefine `clone()`, yet `this.constructor` inside the inherited `clone()` still resolves to `Circle`, so `radius` gets copied too.
Step 4 of 6
Rectangle shows the same reuse with different extra fields
Like `Circle`, `Rectangle` adds its own fields (`width`, `height`) and relies entirely on `Shape.clone()` - no subclass needs to implement cloning itself.
Step 5 of 6
Cloning an existing Circle produces a second, independent Circle
`original.clone()` returns a new object built from `original`'s current field values, without re-running any custom setup logic or knowing `Circle`'s constructor signature.
Step 6 of 6
The clone is independent, and it kept its concrete type
Changing `copy.color` doesn't affect `original.color`, proving the fields were copied rather than shared - and `copy instanceof Circle` prints `true`, showing the clone is a real `Circle`, not a generic `Shape`.
interface Cloneable {
clone(): this;
}
class Shape implements Cloneable {
constructor(
public x: number = 0,
public y: number = 0,
public color: string = 'black',
) {}
clone(): this {
return Object.assign(Object.create(Object.getPrototypeOf(this)), this);
}
}
class Circle extends Shape {
constructor(
x: number,
y: number,
color: string,
public radius: number = 0,
) {
super(x, y, color);
}
}
class Rectangle extends Shape {
constructor(
x: number,
y: number,
color: string,
public width: number = 0,
public height: number = 0,
) {
super(x, y, color);
}
}
const original = new Circle(10, 20, 'red', 15);
const copy = original.clone();
copy.color = 'blue';
console.log(original.color);
console.log(copy.color);
console.log(copy instanceof Circle);
abstract class Shape implements Cloneable {
protected double x, y;
protected String color;
protected Shape(double x, double y, String color) {
this.x = x; this.y = y; this.color = color;
}
@Override
public Shape clone() {
try { return (Shape) super.clone(); }
catch (CloneNotSupportedException e) { throw new AssertionError(); }
}
}
class Circle extends Shape {
public double radius;
Circle(double x, double y, String color, double radius) {
super(x, y, color); this.radius = radius;
}
}
class Rectangle extends Shape {
public double width, height;
Rectangle(double x, double y, String color, double width, double height) {
super(x, y, color); this.width = width; this.height = height;
}
}
Circle original = new Circle(10, 20, "red", 15);
Circle copy = (Circle) original.clone();
copy.color = "blue";
System.out.println(original.color);
System.out.println(copy.color);
System.out.println(copy instanceof Circle);
abstract class Shape
{
public double X { get; set; }
public double Y { get; set; }
public string Color { get; set; }
protected Shape(double x, double y, string color)
{ X = x; Y = y; Color = color; }
public virtual Shape Clone() => (Shape) MemberwiseClone();
}
class Circle : Shape
{
public double Radius { get; set; }
public Circle(double x, double y, string color, double radius)
: base(x, y, color) => Radius = radius;
}
class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public Rectangle(double x, double y, string color, double width, double height)
: base(x, y, color) { Width = width; Height = height; }
}
var original = new Circle(10, 20, "red", 15);
var copy = (Circle) original.Clone();
copy.Color = "blue";
Console.WriteLine(original.Color);
Console.WriteLine(copy.Color);
Console.WriteLine(copy is Circle);
from __future__ import annotations
import copy
class Shape:
def __init__(self, x: float = 0, y: float = 0, color: str = 'black') -> None:
self.x = x
self.y = y
self.color = color
def clone(self) -> Shape:
return copy.copy(self)
class Circle(Shape):
def __init__(self, x: float, y: float, color: str, radius: float = 0) -> None:
super().__init__(x, y, color)
self.radius = radius
class Rectangle(Shape):
def __init__(self, x: float, y: float, color: str,
width: float = 0, height: float = 0) -> None:
super().__init__(x, y, color)
self.width = width
self.height = height
original = Circle(x=10, y=20, color='red', radius=15)
copy_ = original.clone()
copy_.color = 'blue'
print(original.color)
print(copy_.color)
print(type(copy_).__name__)
Step 1 of 6
Shape holds the fields every clone will copy
`__init__` sets `x`, `y`, and `color` as plain instance attributes - exactly the state that `clone()` needs to duplicate.
Step 2 of 6
clone() delegates to Python's own copy machinery
`copy.copy(self)` produces a new object of the same class with the same attribute values, using the standard library's shallow-copy support instead of a hand-written field-by-field constructor call.
Step 3 of 6
Circle extends Shape with its own field, clone() is inherited unchanged
`Circle.__init__` adds `radius` and calls `super().__init__()` for the rest - it never redefines `clone()`, and `copy.copy()` still preserves the concrete `Circle` type along with `radius`.
Step 4 of 6
Rectangle shows the same reuse with different extra fields
`Rectangle` adds `width` and `height` in its own `__init__` and, like `Circle`, relies entirely on the inherited `Shape.clone()` - no subclass reimplements cloning.
Step 5 of 6
Cloning an existing Circle produces a second, independent Circle
`original.clone()` returns a new object with `original`'s current attribute values, without calling `Circle.__init__` again or needing to know its constructor signature.
Step 6 of 6
The clone is independent, and it kept its concrete type
Changing `copy_.color` leaves `original.color` untouched, showing the values were copied rather than shared - and `type(copy_).__name__` prints `Circle`, confirming the clone kept its concrete class.
Click "Run" to execute this code in a sandboxed frame and see console output here.
TypeScript runs as plain JavaScript here - type annotations are stripped, not type-checked.
Click "Run" to execute this code in a sandboxed frame and see console output here.
Advantages
- Code that duplicates an object never has to name its concrete class - it only relies on the shared cloning interface.
- Expensive setup work - loading data, running configuration logic, computing derived state - is paid once and then copied, instead of repeated for every new instance.
- Runtime-built configurations can be captured and reused as living templates, which plain constructors can't replicate since some of that state was never serialized anywhere.
- Producing many pre-configured variants no longer requires a subclass per variant - a library of prototype instances covers the same ground.
Disadvantages
- Getting deep copies right for objects with circular references or shared nested structures takes careful, easy-to-get-wrong logic inside clone().
- Every class in a hierarchy needs to implement or correctly inherit its own cloning logic, which is easy to forget when a new subclass is added.
- A clone that silently shares a mutable nested object with its source can produce hard-to-trace bugs where changes to one instance leak into the other.
Question 1 of 10
According to Prototype's pros, how does a library of prototype instances compare to a subclass per variant?
Correct! Producing many pre-configured variants no longer requires a subclass per variant - a library of prototype instances covers the same ground.
Not quite. Producing many pre-configured variants no longer requires a subclass per variant - a library of prototype instances covers the same ground.
Question 2 of 10
Why does calling `new Circle(x, y, color, radius)` to copy a shape become brittle?
Correct! It gets brittle the moment a subclass like Rectangle adds its own fields the caller doesn't know about, and code holding only a Shape reference can't call a concrete constructor at all.
Not quite. It gets brittle the moment a subclass like Rectangle adds its own fields the caller doesn't know about, and code holding only a Shape reference can't call a concrete constructor at all.
Question 3 of 10
What is a stated benefit of Prototype regarding expensive setup work?
Correct! Cloning reuses already-paid-for setup work instead of repeating it, and runtime-built configurations can be captured and reused as living templates.
Not quite. Cloning reuses already-paid-for setup work instead of repeating it, and runtime-built configurations can be captured and reused as living templates.
Question 4 of 10
In the solution, where does the clone() method live, and why does that matter?
Correct! Because clone() lives inside the class being copied, the caller never enumerates constructor arguments by hand - it just calls .clone() and gets back the same concrete type.
Not quite. Because clone() lives inside the class being copied, the caller never enumerates constructor arguments by hand - it just calls .clone() and gets back the same concrete type.
Question 5 of 10
According to Prototype's drawbacks, what is easy to forget when a hierarchy gains a new subclass?
Correct! Every class in a hierarchy needs to implement or correctly inherit its own cloning logic, which is easy to forget when a new subclass is added.
Not quite. Every class in a hierarchy needs to implement or correctly inherit its own cloning logic, which is easy to forget when a new subclass is added.
Question 6 of 10
How does Prototype create new objects?
Correct! Prototype creates new objects by copying an existing instance, so the copying code never needs to know the concrete type being duplicated.
Not quite. Prototype creates new objects by copying an existing instance, so the copying code never needs to know the concrete type being duplicated.
Question 7 of 10
In the ConcretePrototype's role, what does it decide when implementing clone()?
Correct! Each concrete class implements clone() with direct access to its own private fields, deciding what to deep-copy so the clone doesn't secretly share mutable state with the original.
Not quite. Each concrete class implements clone() with direct access to its own private fields, deciding what to deep-copy so the clone doesn't secretly share mutable state with the original.
Question 8 of 10
In the drawing app problem, what kind of work is a shape put through after it's first placed?
Correct! The drawing app builds shapes at runtime - a Circle placed and dragged into position, recolored, resized - and producing a copy of that current state is what motivates the pattern.
Not quite. The drawing app builds shapes at runtime - a Circle placed and dragged into position, recolored, resized - and producing a copy of that current state is what motivates the pattern.
Question 9 of 10
Why can't code holding only a Shape reference construct a copy with `new`?
Correct! Code that only holds the shape through a common Shape reference has no way to call new Circle(...) or new Rectangle(...) on it, since it doesn't know which concrete subclass it's holding.
Not quite. Code that only holds the shape through a common Shape reference has no way to call new Circle(...) or new Rectangle(...) on it, since it doesn't know which concrete subclass it's holding.
Question 10 of 10
What is a stated risk of cloning objects with shared nested structures?
Correct! Getting deep copies right for circular references or shared nested structures takes careful logic inside clone() - get it wrong and mutations leak between original and copy.
Not quite. Getting deep copies right for circular references or shared nested structures takes careful logic inside clone() - get it wrong and mutations leak between original and copy.