Skip to main content

Prototype Pattern

Clone objects to avoid expensive construction and enable flexible object variation

TL;DR

Prototype creates new objects by cloning an existing prototype rather than constructing from scratch. It's valuable when object construction is expensive, when you need many similar objects with small variations, or when runtime configuration determines object types.

Learning Objectives

  • You will be able to distinguish shallow copy from deep copy and know when each applies.
  • You will be able to implement a clone method that correctly duplicates object state.
  • You will be able to recognize scenarios where cloning beats construction for performance.
  • You will be able to use a prototype registry to manage reusable templates.

Motivating Scenario

Creating a detailed graphics shape (loading textures, computing meshes) takes 500ms. Your scene needs 1000 similar shapes. Creating each from scratch takes 500 seconds. Instead, create one prototype, then clone it 999 times in milliseconds. Slight variations (position, rotation) are cheap post-clone adjustments.

Core Concepts

Prototype defines an interface for cloning itself. Concrete prototypes implement clone logic, creating independent copies. Clients request new objects by cloning prototypes rather than calling constructors.

Key elements:

  • Prototype (abstract): declares the clone interface
  • ConcretePrototype: implements clone, creating a copy of itself
  • Client: creates objects by cloning existing prototypes
  • Prototype Registry: optional; manages and retrieves prototypes by name
Prototype structure

Practical Example

import copy
from abc import ABC, abstractmethod

# Abstract Prototype
class Shape(ABC):
def __init__(self):
self.color = "black"
self.x = 0
self.y = 0

@abstractmethod
def clone(self):
pass

@abstractmethod
def draw(self):
pass

# Concrete Prototypes
class Circle(Shape):
def __init__(self):
super().__init__()
self.radius = 10

def clone(self):
return copy.deepcopy(self)

def draw(self):
return f"Circle at ({self.x}, {self.y}), " \
f"radius={self.radius}, color={self.color}"

class Rectangle(Shape):
def __init__(self):
super().__init__()
self.width = 20
self.height = 30

def clone(self):
return copy.deepcopy(self)

def draw(self):
return f"Rectangle at ({self.x}, {self.y}), " \
f"{self.width}x{self.height}, color={self.color}"

# Prototype Registry
class ShapeRegistry:
def __init__(self):
self._shapes = {}

def register(self, name, shape):
self._shapes[name] = shape

def get(self, name):
return self._shapes[name].clone()

# Usage
registry = ShapeRegistry()
circle = Circle()
circle.color = "red"
registry.register("red_circle", circle)

rect = Rectangle()
rect.color = "blue"
registry.register("blue_rect", rect)

# Clone shapes with variations
shape1 = registry.get("red_circle")
shape1.x, shape1.y = 10, 20
print(shape1.draw())

shape2 = registry.get("red_circle")
shape2.x, shape2.y = 50, 60
print(shape2.draw())

shape3 = registry.get("blue_rect")
shape3.x, shape3.y = 100, 100
print(shape3.draw())

When to Use / When Not to Use

Use Prototype when:
  1. Object construction is expensive (resource-intensive, time-consuming)
  2. You need many similar objects with small variations
  3. Runtime type determination is needed (objects register themselves)
  4. Objects contain complex graphs of sub-objects (cloning is faster than rebuilding)
  5. You want to decouple client code from concrete class hierarchies
Consider alternatives when:
  1. Object creation is cheap (just use new)
  2. Deep copying is more expensive than construction (rare but possible)
  3. Objects have external references that shouldn't be cloned (use Factory instead)
  4. You need fine-grained control over what gets cloned (use Builder)
  5. Cloning is semantically unclear or problematic for your domain

Patterns and Pitfalls

Patterns and Pitfalls

Create prototypes on-demand, caching them for reuse.
Shallow copy shares references; deep copy duplicates everything. Wrong choice breaks isolation.
Defer expensive deep copies until the cloned object is modified.

Design Review Checklist

  • Clone method creates a true independent copy, not a reference
  • Deep vs. shallow copy strategy is intentional and documented
  • Cloning is actually cheaper than construction for your use case
  • Objects being cloned have no external dependencies that shouldn't be cloned
  • The clone method returns the same type (or a compatible interface)
  • Prototype registry (if used) cleanly separates prototype management
  • Testing cloned objects confirms they're truly independent
  • Client code calling clone() is clearer than calling constructors

Self-Check

  1. Identify: What objects in your codebase would benefit from cloning instead of construction?
  2. Implement: Write a clone method ensuring deep copy of nested structures.
  3. Benchmark: Measure whether cloning is actually faster than construction in your scenario.
info

One Takeaway: Prototype shines for expensive object creation or when you have many similar objects with small variations. It trades a clone implementation for performance gains and cleaner client code.

Next Steps

  • Study Factory Method for simpler object creation patterns.
  • Learn Builder when variation comes from configuration, not cloning.
  • Explore immutability to make cloning semantically clearer.

References

  • Gang of Four: Design Patterns (Prototype)
  • Refactoring: Improving the Design of Existing Code
  • Joshua Bloch: Effective Java (Object copying and cloning)