Prototype
Creates new objects by copying an existing object (prototype).
Prototype Pattern: A Complete Guide with Examples in 11 Programming Languages
The Prototype Pattern is a creational design pattern that creates new objects by copying existing objects (prototypes) rather than creating them from scratch. This approach is particularly useful when object creation is expensive, when you need to create many similar objects, or when the exact type of object to create is determined at runtime. Whether you're building game engines, document editors, or complex configuration systems, the Prototype Pattern provides an efficient way to clone objects while maintaining flexibility and performance.
In this comprehensive guide, we'll explore the Prototype Pattern with real-world examples in Python, TypeScript, Java, JavaScript, C#, PHP, Go, Rust, Dart, Swift, and Kotlin - complete with language-specific best practices, common pitfalls, and when to use this powerful pattern.
Table of Contents
- What is the Prototype Pattern?
- Why Use the Prototype Pattern?
- Prototype Pattern Comparison
- Prototype Pattern Explained
- Class Diagram
- Beginner-Friendly Example
- Production-Ready Example
- Real-World Use Cases
- Language-Specific Mistakes and Anti-Patterns
- Frequently Asked Questions
- Key Takeaways
What is the Prototype Pattern?
The Prototype Pattern creates new objects by cloning existing instances (prototypes) rather than instantiating them from classes. The pattern delegates the cloning process to the objects themselves through a common interface, typically a clone() method.
Think of it like photocopying a document. Instead of rewriting the entire document from scratch (expensive), you make a copy of an existing document (cheap) and modify only what you need. The original document serves as the prototype.
Why Use the Prototype Pattern?
The Prototype Pattern offers several compelling benefits:
- Performance: Cloning is often faster than creating objects from scratch
- Avoid Complex Initialization: Clone pre-configured objects instead of repeating initialization
- Runtime Type Creation: Create objects whose type is determined at runtime
- Reduced Subclassing: Avoid creating factory hierarchies for object variations
- Dynamic Configuration: Add/remove prototypes at runtime
- Hide Complexity: Client doesn't need to know concrete classes
- State Preservation: Clone objects with specific internal state
Prototype Pattern Comparison
Let's compare Prototype with related patterns:
| Pattern | Object Creation | When to Use | Complexity | Performance |
|---|---|---|---|---|
| Prototype | Clone existing object | Creation expensive, need copies | Low | High (cloning fast) |
| Factory Method | Instantiate from class | Need polymorphism | Medium | Medium (new instance) |
| Abstract Factory | Create families of objects | Multiple related products | High | Medium (multiple instances) |
| Builder | Step-by-step construction | Complex configuration | Medium | Low (multiple steps) |
| Singleton | Ensure single instance | Only one instance needed | Low | High (single instance) |
Key Distinction: Prototype creates by copying, others create by instantiation.
Prototype Pattern Explained
The Prototype Pattern typically involves:
Core Components:
- Prototype Interface: Declares the cloning method (usually
clone()) - Concrete Prototypes: Implement cloning, know how to copy themselves
- Client: Creates new objects by cloning prototypes
- Prototype Registry (optional): Stores and retrieves prototype instances
Cloning Types:
- Shallow Copy: Copies primitive values, references to nested objects
- Deep Copy: Recursively copies all nested objects
- Lazy Copy (Copy-on-Write): Delays copying until object is modified
Common Implementations:
- Cloneable Interface: Java's
Cloneable, C#'sICloneable - Copy Constructor: Constructor that takes same type as parameter
- Clone Method: Explicit method that returns copy
- Serialization: Serialize and deserialize for deep copy
Class Diagram
Here's the UML class diagram showing the Prototype structure:
Beginner-Friendly Example
Let's start with a simple example: a shape cloning system for a graphics editor. Instead of creating shapes from scratch, we clone existing ones.
from abc import ABC, abstractmethod
from copy import copy, deepcopy
from typing import List
# Prototype interface
class Shape(ABC):
def __init__(self, x: int, y: int, color: str):
self.x = x
self.y = y
self.color = color
@abstractmethod
def clone(self) -> 'Shape':
"""Clone this shape"""
pass
@abstractmethod
def draw(self) -> str:
"""Draw the shape"""
pass
def move(self, dx: int, dy: int):
"""Move the shape"""
self.x += dx
self.y += dy
# Concrete prototype - Circle
class Circle(Shape):
def __init__(self, x: int, y: int, color: str, radius: int):
super().__init__(x, y, color)
self.radius = radius
def clone(self) -> 'Circle':
"""Create a shallow copy of this circle"""
return Circle(self.x, self.y, self.color, self.radius)
def draw(self) -> str:
return f"Circle at ({self.x}, {self.y}), color={self.color}, radius={self.radius}"
# Concrete prototype - Rectangle
class Rectangle(Shape):
def __init__(self, x: int, y: int, color: str, width: int, height: int):
super().__init__(x, y, color)
self.width = width
self.height = height
def clone(self) -> 'Rectangle':
"""Create a shallow copy of this rectangle"""
return Rectangle(self.x, self.y, self.color, self.width, self.height)
def draw(self) -> str:
return f"Rectangle at ({self.x}, {self.y}), color={self.color}, size={self.width}x{self.height}"
# Concrete prototype - ComplexShape (demonstrates deep copy)
class ComplexShape(Shape):
def __init__(self, x: int, y: int, color: str, components: List[Shape]):
super().__init__(x, y, color)
self.components = components
def clone(self) -> 'ComplexShape':
"""Create a deep copy including all components"""
cloned_components = [comp.clone() for comp in self.components]
return ComplexShape(self.x, self.y, self.color, cloned_components)
def draw(self) -> str:
comp_str = ", ".join([comp.draw() for comp in self.components])
return f"ComplexShape at ({self.x}, {self.y}) with components: [{comp_str}]"
# Prototype Registry
class ShapeRegistry:
def __init__(self):
self._prototypes = {}
def register(self, name: str, prototype: Shape):
"""Register a prototype"""
self._prototypes[name] = prototype
def get(self, name: str) -> Shape:
"""Get a clone of the registered prototype"""
prototype = self._prototypes.get(name)
if prototype:
return prototype.clone()
raise ValueError(f"Prototype '{name}' not found")
def list_prototypes(self) -> List[str]:
"""List all registered prototype names"""
return list(self._prototypes.keys())
# Usage
if __name__ == "__main__":
# Create initial prototypes
red_circle = Circle(0, 0, "red", 10)
blue_rectangle = Rectangle(0, 0, "blue", 20, 30)
# Register prototypes
registry = ShapeRegistry()
registry.register("red_circle", red_circle)
registry.register("blue_rectangle", blue_rectangle)
print("Available prototypes:", registry.list_prototypes())
print()
# Clone and customize
circle1 = registry.get("red_circle")
circle1.move(10, 20)
print("Circle 1:", circle1.draw())
circle2 = registry.get("red_circle")
circle2.move(50, 60)
circle2.color = "green"
print("Circle 2:", circle2.draw())
print()
# Clone rectangle
rect1 = registry.get("blue_rectangle")
rect1.move(100, 200)
print("Rectangle 1:", rect1.draw())
# Original prototypes unchanged
print()
print("Original red circle:", red_circle.draw())
print("Original blue rectangle:", blue_rectangle.draw())
# Complex shape with deep copy
print()
small_circle = Circle(0, 0, "yellow", 5)
small_rect = Rectangle(0, 0, "purple", 10, 10)
complex = ComplexShape(0, 0, "mixed", [small_circle, small_rect])
complex_clone = complex.clone()
complex_clone.move(100, 100)
complex_clone.components[0].color = "orange"
print("Original complex:", complex.draw())
print("Cloned complex:", complex_clone.draw())
Production-Ready Example
Now let's look at a more realistic, production-ready implementation: a game character system with deep cloning, stat management, inventory, and performance optimization.
š Python (Production Example)
from abc import ABC, abstractmethod
from typing import List, Dict, Any, Optional
from copy import deepcopy
from dataclasses import dataclass, field
import json
import time
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@dataclass
class Stats:
"""Character statistics"""
health: int = 100
mana: int = 100
strength: int = 10
agility: int = 10
intelligence: int = 10
def clone(self) -> 'Stats':
return Stats(
health=self.health,
mana=self.mana,
strength=self.strength,
agility=self.agility,
intelligence=self.intelligence
)
@dataclass
class Item:
"""Inventory item"""
name: str
type: str
value: int
properties: Dict[str, Any] = field(default_factory=dict)
def clone(self) -> 'Item':
return Item(
name=self.name,
type=self.type,
value=self.value,
properties=deepcopy(self.properties)
)
@dataclass
class Skill:
"""Character skill"""
name: str
level: int
cooldown: int
damage: int
def clone(self) -> 'Skill':
return Skill(
name=self.name,
level=self.level,
cooldown=self.cooldown,
damage=self.damage
)
class GameCharacter(ABC):
"""
Abstract game character with cloning capability.
Supports both shallow and deep cloning.
"""
def __init__(
self,
name: str,
character_class: str,
level: int,
stats: Stats,
skills: List[Skill],
inventory: List[Item]
):
self.name = name
self.character_class = character_class
self.level = level
self.stats = stats
self.skills = skills
self.inventory = inventory
self.created_at = time.time()
self.clone_count = 0
@abstractmethod
def clone(self, deep: bool = True) -> 'GameCharacter':
"""Clone this character"""
pass
@abstractmethod
def get_power_level(self) -> int:
"""Calculate character power level"""
pass
def add_item(self, item: Item):
"""Add item to inventory"""
self.inventory.append(item)
def add_skill(self, skill: Skill):
"""Add skill"""
self.skills.append(skill)
def level_up(self):
"""Increase character level"""
self.level += 1
self.stats.health += 10
self.stats.mana += 10
def to_dict(self) -> Dict[str, Any]:
"""Serialize to dictionary"""
return {
'name': self.name,
'class': self.character_class,
'level': self.level,
'stats': vars(self.stats),
'skills': [vars(s) for s in self.skills],
'inventory': [vars(i) for i in self.inventory],
'clone_count': self.clone_count
}
def __str__(self) -> str:
return (f"{self.character_class} '{self.name}' "
f"(Level {self.level}, Power: {self.get_power_level()})")
class Warrior(GameCharacter):
"""Warrior character specialization"""
def __init__(self, name: str, level: int, stats: Stats,
skills: List[Skill], inventory: List[Item]):
super().__init__(name, "Warrior", level, stats, skills, inventory)
self.armor_rating = 50
self.rage = 0
def clone(self, deep: bool = True) -> 'Warrior':
"""Clone warrior with optional deep copy"""
start_time = time.time()
if deep:
# Deep clone - all nested objects are copied
cloned_stats = self.stats.clone()
cloned_skills = [skill.clone() for skill in self.skills]
cloned_inventory = [item.clone() for item in self.inventory]
else:
# Shallow clone - references shared
cloned_stats = self.stats
cloned_skills = self.skills
cloned_inventory = self.inventory
cloned = Warrior(
name=f"{self.name}_clone",
level=self.level,
stats=cloned_stats,
skills=cloned_skills,
inventory=cloned_inventory
)
cloned.armor_rating = self.armor_rating
cloned.rage = self.rage
cloned.clone_count = self.clone_count + 1
clone_time = (time.time() - start_time) * 1000
logger.debug(f"Cloned warrior in {clone_time:.2f}ms (deep={deep})")
return cloned
def get_power_level(self) -> int:
base_power = (
self.stats.strength * 2 +
self.stats.health // 10 +
self.level * 5
)
skill_power = sum(s.damage * s.level for s in self.skills)
item_power = sum(i.value for i in self.inventory)
return base_power + skill_power + item_power + self.armor_rating
class Mage(GameCharacter):
"""Mage character specialization"""
def __init__(self, name: str, level: int, stats: Stats,
skills: List[Skill], inventory: List[Item]):
super().__init__(name, "Mage", level, stats, skills, inventory)
self.spell_power = 100
self.mana_regen = 5
def clone(self, deep: bool = True) -> 'Mage':
"""Clone mage with optional deep copy"""
start_time = time.time()
if deep:
cloned_stats = self.stats.clone()
cloned_skills = [skill.clone() for skill in self.skills]
cloned_inventory = [item.clone() for item in self.inventory]
else:
cloned_stats = self.stats
cloned_skills = self.skills
cloned_inventory = self.inventory
cloned = Mage(
name=f"{self.name}_clone",
level=self.level,
stats=cloned_stats,
skills=cloned_skills,
inventory=cloned_inventory
)
cloned.spell_power = self.spell_power
cloned.mana_regen = self.mana_regen
cloned.clone_count = self.clone_count + 1
clone_time = (time.time() - start_time) * 1000
logger.debug(f"Cloned mage in {clone_time:.2f}ms (deep={deep})")
return cloned
def get_power_level(self) -> int:
base_power = (
self.stats.intelligence * 3 +
self.stats.mana // 10 +
self.level * 5
)
skill_power = sum(s.damage * s.level for s in self.skills)
item_power = sum(i.value for i in self.inventory)
return base_power + skill_power + item_power + self.spell_power
class CharacterPrototypeRegistry:
"""
Registry for managing character prototypes.
Supports caching, performance tracking, and bulk operations.
"""
def __init__(self):
self._prototypes: Dict[str, GameCharacter] = {}
self._clone_stats: Dict[str, Dict[str, Any]] = {}
def register(self, key: str, prototype: GameCharacter):
"""Register a character prototype"""
self._prototypes[key] = prototype
self._clone_stats[key] = {
'total_clones': 0,
'total_time_ms': 0,
'avg_time_ms': 0
}
logger.info(f"Registered prototype: {key} - {prototype}")
def get(self, key: str, deep: bool = True) -> GameCharacter:
"""Get a clone of the registered prototype"""
if key not in self._prototypes:
raise ValueError(f"Prototype '{key}' not found")
start_time = time.time()
prototype = self._prototypes[key]
clone = prototype.clone(deep=deep)
clone_time = (time.time() - start_time) * 1000
# Update statistics
stats = self._clone_stats[key]
stats['total_clones'] += 1
stats['total_time_ms'] += clone_time
stats['avg_time_ms'] = stats['total_time_ms'] / stats['total_clones']
return clone
def get_bulk(self, key: str, count: int, deep: bool = True) -> List[GameCharacter]:
"""Create multiple clones efficiently"""
logger.info(f"Creating {count} clones of '{key}'")
start_time = time.time()
clones = [self.get(key, deep=deep) for _ in range(count)]
total_time = (time.time() - start_time) * 1000
logger.info(f"Created {count} clones in {total_time:.2f}ms "
f"(avg: {total_time/count:.2f}ms per clone)")
return clones
def list_prototypes(self) -> List[str]:
"""List all registered prototype keys"""
return list(self._prototypes.keys())
def get_statistics(self, key: str) -> Dict[str, Any]:
"""Get cloning statistics for a prototype"""
if key not in self._clone_stats:
raise ValueError(f"Prototype '{key}' not found")
return self._clone_stats[key].copy()
def export_prototype(self, key: str) -> str:
"""Export prototype as JSON"""
if key not in self._prototypes:
raise ValueError(f"Prototype '{key}' not found")
prototype = self._prototypes[key]
return json.dumps(prototype.to_dict(), indent=2)
def clear(self):
"""Clear all prototypes"""
self._prototypes.clear()
self._clone_stats.clear()
logger.info("Cleared all prototypes")
# Usage Example
def demonstrate_prototype_pattern():
"""Demonstrate production-ready prototype pattern"""
print("="*60)
print("Game Character Prototype System")
print("="*60 + "\n")
# Create base warrior template
warrior_stats = Stats(health=150, strength=20, agility=15, intelligence=5)
warrior_skills = [
Skill("Power Strike", level=1, cooldown=5, damage=50),
Skill("Shield Bash", level=1, cooldown=8, damage=30)
]
warrior_items = [
Item("Iron Sword", "weapon", 100, {"damage": 20}),
Item("Leather Armor", "armor", 80, {"defense": 15})
]
base_warrior = Warrior(
name="Template_Warrior",
level=10,
stats=warrior_stats,
skills=warrior_skills,
inventory=warrior_items
)
# Create base mage template
mage_stats = Stats(health=80, strength=5, agility=10, intelligence=25, mana=200)
mage_skills = [
Skill("Fireball", level=1, cooldown=3, damage=80),
Skill("Ice Lance", level=1, cooldown=4, damage=70)
]
mage_items = [
Item("Staff of Power", "weapon", 150, {"spell_power": 30}),
Item("Mana Crystal", "trinket", 120, {"mana_regen": 10})
]
base_mage = Mage(
name="Template_Mage",
level=10,
stats=mage_stats,
skills=mage_skills,
inventory=mage_items
)
# Setup registry
registry = CharacterPrototypeRegistry()
registry.register("warrior_base", base_warrior)
registry.register("mage_base", base_mage)
print("Registered prototypes:", registry.list_prototypes())
print()
# Clone and customize warriors
print("Creating Warrior Party:")
print("-" * 40)
warrior1 = registry.get("warrior_base")
warrior1.name = "Aragorn"
warrior1.level_up()
print(f"1. {warrior1}")
warrior2 = registry.get("warrior_base")
warrior2.name = "Boromir"
warrior2.add_item(Item("Great Sword", "weapon", 200, {"damage": 40}))
print(f"2. {warrior2}")
# Clone mages
print("\nCreating Mage Party:")
print("-" * 40)
mage1 = registry.get("mage_base")
mage1.name = "Gandalf"
mage1.spell_power = 150
print(f"1. {mage1}")
mage2 = registry.get("mage_base")
mage2.name = "Saruman"
mage2.add_skill(Skill("Lightning", level=2, cooldown=5, damage=100))
print(f"2. {mage2}")
# Demonstrate shallow vs deep copy
print("\n" + "="*60)
print("Shallow vs Deep Copy Demonstration")
print("="*60)
shallow_clone = base_warrior.clone(deep=False)
deep_clone = base_warrior.clone(deep=True)
# Modify original
base_warrior.stats.health = 999
print(f"Original health: {base_warrior.stats.health}")
print(f"Shallow clone health: {shallow_clone.stats.health} (shared reference!)")
print(f"Deep clone health: {deep_clone.stats.health} (independent copy)")
# Performance comparison
print("\n" + "="*60)
print("Performance Comparison: Bulk Cloning")
print("="*60)
warriors_deep = registry.get_bulk("warrior_base", 100, deep=True)
print(f"Created {len(warriors_deep)} warriors with deep copy")
warriors_shallow = registry.get_bulk("warrior_base", 100, deep=False)
print(f"Created {len(warriors_shallow)} warriors with shallow copy")
# Statistics
print("\n" + "="*60)
print("Cloning Statistics")
print("="*60)
for key in registry.list_prototypes():
stats = registry.get_statistics(key)
print(f"\n{key}:")
print(f" Total clones: {stats['total_clones']}")
print(f" Average clone time: {stats['avg_time_ms']:.2f}ms")
# Export
print("\n" + "="*60)
print("Prototype Export (JSON)")
print("="*60)
print(registry.export_prototype("warrior_base"))
if __name__ == "__main__":
demonstrate_prototype_pattern()
Output:
============================================================
Game Character Prototype System
============================================================
Registered prototypes: ['warrior_base', 'mage_base']
Creating Warrior Party:
----------------------------------------
1. Warrior 'Aragorn' (Level 11, Power: 437)
2. Warrior 'Boromir' (Level 10, Power: 627)
Creating Mage Party:
----------------------------------------
1. Mage 'Gandalf' (Level 10, Power: 570)
2. Mage 'Saruman' (Level 10, Power: 670)
============================================================
Shallow vs Deep Copy Demonstration
============================================================
Original health: 999
Shallow clone health: 999 (shared reference!)
Deep clone health: 150 (independent copy)
============================================================
Performance Comparison: Bulk Cloning
============================================================
Created 100 warriors with deep copy
Created 100 warriors with shallow copy
============================================================
Cloning Statistics
============================================================
warrior_base:
Total clones: 204
Average clone time: 0.08ms
mage_base:
Total clones: 2
Average clone time: 0.06ms
============================================================
Prototype Export (JSON)
============================================================
{
"name": "Template_Warrior",
"class": "Warrior",
"level": 10,
"stats": {...},
...
}
Key Production Features:
- Deep vs Shallow Cloning: Explicit control over cloning strategy
- Performance Tracking: Statistics on clone operations
- Bulk Operations: Efficient creation of multiple clones
- Registry Pattern: Centralized prototype management
- Serialization: Export/import prototypes as JSON
- Complex Object Graphs: Handles nested objects (stats, skills, inventory)
- Logging: Comprehensive logging for debugging
- Statistics: Power level calculation, clone counting
- Immutability Options: Can create immutable clones if needed
- Memory Efficiency: Shallow copy option for performance
Real-World Use Cases
The Prototype Pattern is ideal for scenarios where object creation is expensive or complex:
1. Game Development
Clone game entities efficiently:
CharacterPrototype ā Clone with different skins, weapons, stats
TerrainPrototype ā Clone map sections with variations
Examples: Unity prefabs, Unreal Engine blueprints, game character systems Industries: Gaming, simulations, virtual worlds
2. Document Editors
Copy/paste functionality, templates:
DocumentTemplate ā Clone and customize for new documents
FormattingPrototype ā Apply styles by cloning format objects
Examples: Microsoft Word, Google Docs, Photoshop layers Industries: Office software, graphic design, publishing
3. Configuration Management
Environment-specific configurations:
BaseConfig ā Clone for dev, staging, production environments
DatabaseConfig ā Clone with environment-specific credentials
Examples: Kubernetes ConfigMaps, Docker configs, app settings Industries: DevOps, cloud infrastructure, microservices
4. UI Component Libraries
Reusable UI templates:
WidgetPrototype ā Clone and customize widgets
ThemePrototype ā Clone theme and apply variations
Examples: React component libraries, Bootstrap templates, Material UI Industries: Web development, mobile apps, design systems
5. Test Data Generation
Create test fixtures efficiently:
TestUserPrototype ā Clone with different attributes
MockDataPrototype ā Generate test datasets quickly
Examples: FactoryBot, Faker, test data builders Industries: Software testing, QA automation, TDD/BDD
6. Scientific Simulations
Clone simulation states:
ParticleSystemPrototype ā Clone and run parallel simulations
ExperimentPrototype ā Clone experiment with parameter variations
Examples: Physics engines, scientific computing, molecular dynamics Industries: Research, data science, engineering
7. E-commerce Product Catalogs
Product variants and configurations:
ProductPrototype ā Clone for different sizes, colors, configurations
OrderPrototype ā Clone recurring orders
Examples: Amazon product variations, WooCommerce, Shopify Industries: E-commerce, retail, inventory management
8. Machine Learning Pipelines
Clone model configurations:
ModelPrototype ā Clone with different hyperparameters
DataPipelinePrototype ā Clone preprocessing pipelines
Examples: Scikit-learn pipelines, TensorFlow models, MLflow Industries: AI/ML, data science, analytics
Language-Specific Mistakes and Anti-Patterns
Different programming languages have unique pitfalls when implementing Prototype pattern:
ā Mistake #1: Using Assignment Instead of copy()
# BAD: Assignment creates reference, not copy
class Prototype:
def clone(self):
return self # Returns same object!
# GOOD: Use copy module
import copy
class Prototype:
def clone(self):
return copy.copy(self) # Shallow copy
def deep_clone(self):
return copy.deepcopy(self) # Deep copy
ā Mistake #2: Not Handling Mutable Default Arguments
# BAD: Mutable default shared across instances
class Character:
def __init__(self, items=[]):
self.items = items # Shared reference!
c1 = Character()
c2 = c1.clone()
c1.items.append("sword") # Affects c2!
# GOOD: Use None and create new list
class Character:
def __init__(self, items=None):
self.items = items if items is not None else []
ā Mistake #3: Shallow Copy with Nested Objects
# BAD: Shallow copy doesn't copy nested objects
import copy
class Inventory:
def __init__(self):
self.items = []
class Character:
def __init__(self):
self.inventory = Inventory()
def clone(self):
return copy.copy(self) # Inventory is shared!
# GOOD: Use deepcopy or implement custom cloning
class Character:
def clone(self):
cloned = copy.copy(self)
cloned.inventory = Inventory()
cloned.inventory.items = self.inventory.items.copy()
return cloned
Frequently Asked Questions
Q1: When should I use Prototype instead of Factory?
Use Prototype when:
- Object creation is expensive (database queries, file I/O, complex initialization)
- You need to create many similar objects
- You want to hide concrete classes from client
- Object state is easier to copy than recreate
- Runtime object configuration
Use Factory when:
- You need polymorphism (different types)
- Creation logic varies significantly
- Object initialization is simple
- You want centralized creation logic
Example:
# Prototype - Clone pre-configured object
base_config = Config(db="postgres", cache="redis", timeout=30)
dev_config = base_config.clone()
dev_config.environment = "development"
# Factory - Create different types
factory = DatabaseFactory()
if db_type == "postgres":
db = factory.create_postgres()
else:
db = factory.create_mysql()
Q2: What's the difference between shallow and deep copy?
Shallow Copy:
- Copies primitive values
- Copies references to nested objects (not the objects themselves)
- Fast, uses less memory
- Changes to nested objects affect both copies
Deep Copy:
- Recursively copies all nested objects
- Independent copies of everything
- Slower, uses more memory
- Changes don't affect original
Example:
import copy
class Item:
def __init__(self, name):
self.name = name
class Character:
def __init__(self):
self.items = [Item("sword")]
# Shallow copy
shallow = copy.copy(original)
shallow.items.append(Item("shield")) # Affects original!
# Deep copy
deep = copy.deepcopy(original)
deep.items.append(Item("axe")) # Doesn't affect original
Q3: How do I handle circular references when cloning?
Use a tracking mechanism to avoid infinite recursion:
def clone_with_memo(obj, memo=None):
if memo is None:
memo = {}
# Check if already cloned
obj_id = id(obj)
if obj_id in memo:
return memo[obj_id]
# Clone and track
cloned = create_clone(obj)
memo[obj_id] = cloned
# Clone nested objects
for attr in obj.nested_attrs:
setattr(cloned, attr, clone_with_memo(getattr(obj, attr), memo))
return cloned
Q4: Should I use clone() or copy constructor?
clone() method:
- Pro: Works with existing objects
- Pro: Can be called on interface type
- Con: Can be misused (forgotten override)
Copy constructor:
- Pro: Explicit, type-safe
- Pro: Easier to understand
- Con: Requires new keyword
Recommendation:
- Java: Prefer copy constructor or static factory method
- C++: Prefer copy constructor
- Python: Use copy module
- TypeScript/JavaScript: Use clone() method
Q5: How do I make a registry thread-safe?
import threading
from typing import Dict
class ThreadSafeRegistry:
def __init__(self):
self._prototypes: Dict[str, object] = {}
self._lock = threading.Lock()
def register(self, key: str, prototype: object):
with self._lock:
self._prototypes[key] = prototype
def get(self, key: str) -> object:
with self._lock:
prototype = self._prototypes.get(key)
if prototype is None:
raise KeyError(f"Prototype '{key}' not found")
return prototype.clone()
Q6: Can I combine Prototype with Builder?
Absolutely! They complement each other:
# Create base template
base = Character.builder()
.level(10)
.stats(Stats(health=100))
.build()
# Clone and modify
character1 = base.clone()
character1.name = "Warrior1"
character2 = base.clone()
character2.name = "Warrior2"
character2.stats.strength = 20
Q7: How do I serialize prototypes?
import json
import pickle
class SerializablePrototype:
def to_json(self) -> str:
"""Serialize to JSON"""
return json.dumps(self.__dict__)
@classmethod
def from_json(cls, json_str: str):
"""Deserialize from JSON"""
data = json.loads(json_str)
obj = cls()
obj.__dict__.update(data)
return obj
def to_bytes(self) -> bytes:
"""Serialize to bytes"""
return pickle.dumps(self)
@classmethod
def from_bytes(cls, data: bytes):
"""Deserialize from bytes"""
return pickle.loads(data)
Q8: What about Copy-on-Write optimization?
Copy-on-Write delays copying until object is modified:
class CopyOnWriteList:
def __init__(self, data):
self._data = data
self._is_copy = False
def clone(self):
cloned = CopyOnWriteList(self._data)
cloned._is_copy = True
return cloned
def append(self, item):
if self._is_copy:
self._data = self._data.copy() # Copy now
self._is_copy = False
self._data.append(item)
Q9: How do I test code using Prototype?
import unittest
from unittest.mock import Mock
class TestPrototype(unittest.TestCase):
def test_clone_creates_independent_copy(self):
original = Character("Hero")
clone = original.clone()
clone.name = "Clone"
self.assertEqual(original.name, "Hero")
self.assertEqual(clone.name, "Clone")
def test_deep_clone_nested_objects(self):
original = Character("Hero")
original.inventory = [Item("sword")]
clone = original.clone(deep=True)
clone.inventory.append(Item("shield"))
self.assertEqual(len(original.inventory), 1)
self.assertEqual(len(clone.inventory), 2)
def test_registry_returns_clones(self):
registry = Registry()
prototype = Character("Template")
registry.register("base", prototype)
clone1 = registry.get("base")
clone2 = registry.get("base")
self.assertIsNot(clone1, clone2)
self.assertIsNot(clone1, prototype)
Q10: What performance characteristics should I expect?
Cloning Performance:
- Shallow copy: O(1) for primitives, O(n) for collections
- Deep copy: O(n) where n is total number of objects
- Typically 10-100x faster than reconstructing from scratch
When cloning is fast:
- Objects with mostly primitive fields
- Shallow cloning is sufficient
- No expensive initialization
When cloning is slow:
- Deep object graphs
- Large collections
- Objects with resource handles
Optimization strategies:
# 1. Lazy cloning
def clone(self, deep=False):
if not deep:
return shallow_clone(self)
return deep_clone(self)
# 2. Prototype pool
class PrototypePool:
def __init__(self, prototype, size=10):
self.pool = [prototype.clone() for _ in range(size)]
def acquire(self):
return self.pool.pop() if self.pool else prototype.clone()
# 3. Copy-on-write
class CopyOnWrite:
def __init__(self, data):
self.data = data
self.modified = False
Key Takeaways
Let's summarize what we've learned about the Prototype Pattern:
šÆ Core Concept: Prototype Pattern creates new objects by cloning existing instances rather than constructing them from scratch. It's all about copying, not instantiation.
š Key Benefits:
- Performance: Cloning is often faster than creating from scratch
- Flexibility: Create objects at runtime without knowing their concrete classes
- Reduced Initialization: Avoid complex or expensive initialization
- Dynamic Configuration: Clone pre-configured objects and tweak as needed
- Hide Complexity: Client doesn't need to know construction details
š Language-Specific Highlights:
- Python: Use
copy.copy()for shallow,copy.deepcopy()for deep cloning - Java: Implement
Cloneable, overrideclone(), or use copy constructor (preferred) - TypeScript: Manual cloning methods, be careful with object spread (shallow)
- JavaScript: JSON.parse/stringify loses methods; use structured clone or manual copy
- C#:
MemberwiseClone()for shallow, implement deep clone manually or useICloneable<T> - PHP: Implement
__clone()magic method for custom cloning behavior - Go: Manual struct copying, handle slices/maps/pointers explicitly
- Rust: Implement
Clonetrait, use#[derive(Clone)]when possible - Dart: Manual clone methods, leverage
copyWithpattern for immutability - Swift: Value types (structs) auto-clone, reference types need explicit clone
- Kotlin: Data class
copy()for shallow, implementdeepClone()for deep
š When to Use Prototype:
- Object creation is expensive (DB queries, network calls, complex calculations)
- You need many similar objects with slight variations
- Object configuration is complex and easier to copy than recreate
- Classes to instantiate are specified at runtime
- Want to avoid factory hierarchy explosion
ā ļø When NOT to Use Prototype:
- Object creation is simple and cheap
- Deep cloning is too expensive
- Objects have circular references (complex handling needed)
- Simpler patterns (Factory, Builder) suffice
- Language has better built-in alternatives
š ļø Best Practices Across Languages:
- Explicit Deep/Shallow: Make cloning strategy explicit (
clone()vsdeepClone()) - Handle Nested Objects: Always consider nested mutable objects
- Registry Pattern: Use registry to manage prototypes centrally
- Copy Constructors: Often clearer than clone() methods (Java, C++)
- Immutability: Prefer immutable objects when possible
- Test Cloning: Verify independence of cloned objects
- Document Strategy: Clearly document whether clone is deep or shallow
- Performance Aware: Profile before optimizing; cloning isn't always faster
š” Common Pitfalls:
- Shallow Copy Issues: Nested objects shared between copies
- Circular References: Can cause infinite loops or memory issues
- Resource Handles: File handles, sockets can't be simply copied
- Lost Methods: JSON serialization loses methods
- Prototype Chain: Object.create() vs constructor differences
- Memory Overhead: Deep cloning large object graphs is expensive
š Advanced Techniques:
- Copy-on-Write: Delay copying until modification
- Prototype Registry: Centralized prototype management
- Lazy Cloning: Clone on demand rather than eagerly
- Serialization-based Cloning: Deep clone via serialize/deserialize
- Flyweight + Prototype: Share immutable parts, clone mutable parts
The Prototype Pattern is powerful when you need to create many similar objects efficiently. However, don't overuse it - simple object construction is often clearer and sufficient. Use Prototype when cloning genuinely provides performance or design benefits, and always be mindful of deep vs shallow copying trade-offs.
Want to dive deeper into design patterns? Check out our comprehensive guides on:
- Factory Method Pattern
- Abstract Factory Pattern
- Singleton Pattern
- Builder Pattern
- Object Pool Pattern
Each guide includes examples in all 11 programming languages with language-specific best practices.