Skip to main content

Prototype

complexitypopularity

Let objects copy themselves through a shared clone method, so client code duplicates them without knowing their concrete classes.

The problem

You have an object and you want an exact copy. The obvious move is to make a new object of the same class and copy the fields across. Two things go wrong immediately.

Some of those fields are private and simply not visible from outside. And to write the copy at all you must name the concrete class, which welds your code to it - awkward, given you often only know the interface the object implements, not what it actually is.

The solution

Delegate the copying to the object itself. Declare a common interface for anything copyable - usually a single clone() method - and let each class implement it. Inside that method the class can read its own private fields, because most languages let an object touch private members of other instances of the same class.

The implementation is boringly uniform: a copy constructor that carries over the fields, and a clone() that returns new MyClass(this). Subclasses call the parent copy constructor first so inherited fields come along.

There is a second benefit that has nothing to do with privacy. When objects have dozens of fields and hundreds of plausible configurations, cloning replaces subclassing. Build a set of configured instances once, then clone whichever one you want instead of constructing from scratch. Keep them in a prototype registry - at its simplest, a name to prototype map - and pre-built configurations become lookups.

Editor (client)ShapeRegistryStored prototypeFresh cloneput("bigBlueCircle", circle)1get("bigBlueCircle")2clone()3new Circle(this)4returns Shape5clone() on each item6
  1. 1At startup a few painstakingly configured instances are filed in the registry under readable names.
  2. 2Later, the client asks for that configuration by name. No constructor, no parameters, no subclass.
  3. 3The registry never hands out the stored object itself. It always returns a copy, so the catalog stays pristine.
  4. 4The prototype passes itself to its own copy constructor, which can read private fields because same-class access is allowed.
  5. 5The clone arrives typed as Shape. The client got a fully configured Circle without ever saying the word Circle.
  6. 6The same trick scales: iterate a mixed array, call clone() on every element, and polymorphism picks the right implementation each time.

Structure

The basic version is two boxes: a prototype interface declaring clone(), and concrete prototypes implementing it. The registry variant adds a catalog in front, storing ready-made instances and returning copies of them.

«abstract»Shapex, y, colorShape(source)clone(): ShapePROTOTYPECircleradiusCircle(source)clone(): ShapeCONCRETERectanglewidth, heightRectangle(source)clone(): ShapeCONCRETEShapeRegistryitems: mapput(key, shape)get(key): ShapePROTOTYPEEditorshapes: array of ShapeduplicateAll()CLIENT
extendsuses

Code

Same example three ways: a shape hierarchy that duplicates itself, plus a registry of pre-configured shapes.

// Copying an object "from the outside" goes badly.
method duplicate(shape) is
// 1. You must know the concrete class to instantiate it.
if (shape is Circle) then
copy = new Circle()
copy.x = shape.x
copy.y = shape.y
copy.color = shape.color
copy.radius = shape.radius
// 2. And any private field is simply unreachable from here.
else if (shape is Rectangle) then
// ...the same tedium, again, with different fields.
 
// 3. Every new shape class means another branch in this method,
// which lives nowhere near the class that changed.
// Cloning is the object's own responsibility now.
class Circle extends Shape is
method clone(): Shape is
return new Circle(this)
 
// So the client copies anything, knowing nothing:
foreach (s in shapes) do
copies.add(s.clone())
 
// And pre-configured setups become catalog entries instead of subclasses:
registry.put("bigBlueCircle", circle)
Shape another = registry.get("bigBlueCircle")
// No instanceof, no switch, no BigBlueCircle class.
// The base prototype declares clone() and knows how to copy its
// own fields, private ones included.
abstract class Shape is
field x: int
field y: int
field color: string
 
constructor Shape() is
// The ordinary constructor.
 
// The prototype constructor: seed a fresh object from an existing one.
constructor Shape(source: Shape) is
this()
this.x = source.x
this.y = source.y
this.color = source.color
 
abstract method clone(): Shape
 
// Each concrete prototype copies its own extra fields and then
// returns an object of its own class. Both halves matter.
class Circle extends Shape is
field radius: int
 
constructor Circle(source: Circle) is
// The parent call is what copies the fields Circle cannot see.
super(source)
this.radius = source.radius
 
method clone(): Shape is
return new Circle(this)
 
class Rectangle extends Shape is
field width: int
field height: int
 
constructor Rectangle(source: Rectangle) is
super(source)
this.width = source.width
this.height = source.height
 
method clone(): Shape is
return new Rectangle(this)
 
// Optional but useful: a catalog of pre-configured prototypes.
class ShapeRegistry is
private field items: hash map of names and Shapes
 
method put(key, shape: Shape) is
items[key] = shape
 
// Hand out a copy, never the catalog entry itself.
method get(key): Shape is
return items[key].clone()
 
// The client copies things it cannot name.
class Editor is
field shapes: array of Shape
 
constructor Editor(registry: ShapeRegistry) is
circle = new Circle()
circle.x = 10
circle.y = 10
circle.radius = 20
shapes.add(circle)
shapes.add(circle.clone()) // An exact copy, no questions asked.
 
registry.put("bigBlueCircle", circle)
 
method duplicateAll() is
copies = new array of Shape
// We do not know what is in here. We do not need to: each
// element resolves clone() to its own real class.
foreach (s in shapes) do
copies.add(s.clone())
return copies

When to use it

  • Your code must copy objects whose concrete classes it does not and should not know, which happens constantly with objects handed to you by third-party code behind an interface.
  • You are accumulating subclasses that differ only in how they initialize themselves. Replace them with configured prototypes and a registry lookup.
  • Reconstructing a complex object is expensive or fiddly, and copying a known-good one is both cheaper and less likely to be wrong.

Pitfalls

  • Shallow by default. A field-by-field copy duplicates references, not the things they point at. Two "independent" clones sharing one mutable list is a fun afternoon of debugging.
  • Circular references. Object graphs that point back at themselves make naive deep copies recurse forever. You need visited-set bookkeeping or a serialization trick.
  • The forgotten override. A subclass without its own clone() silently produces a parent-class object. Nothing complains until something downstream does.
  • Registries that hand out the original. If get() returns the stored instance instead of a copy, the first caller to mutate it corrupts the catalog for everyone.

Don't confuse it with

  • Copy constructors. A copy constructor is the mechanism Prototype uses; Prototype is the polymorphic interface wrapped around it. Without clone(), the caller is back to naming classes.
  • Factory Method. Prototype is not built on inheritance, so it dodges inheritance's drawbacks, but it requires a properly initialized object to copy. Factory Method needs the hierarchy and skips the setup.
  • Memento. For snapshotting simple objects with no external links, a clone is often a perfectly good and much cheaper Memento.

Check yourself

Question 1 of 5

Why does Prototype put the copying logic inside the object being copied?