Prototype Pattern¶
Category: Creational Pattern
Overview¶
Create new objects by cloning existing instances rather than creating new ones from scratch. This pattern is useful when object creation is expensive or when you want to avoid the complexity of instantiating an object directly, leveraging Python's built-in copy module for shallow and deep copying.
Usage Guidelines¶
Use when:
- Creating new objects is more expensive than cloning existing ones
- Objects require complex setup that can be reused
- Types to create are determined at runtime
- Need copies of objects in specific states
Avoid when:
- Creating new objects is straightforward and cheap
- Complexity of managing object references outweighs benefits
- Objects contain circular references that complicate cloning
- Objects are immutable and can be safely shared
Implementation¶
from __future__ import annotations
import copy
from typing import Any
class Prototype:
"""Abstract base class for prototypes.
Defines the interface for cloning objects.
"""
def clone(self) -> Prototype:
"""Create a shallow copy of the object.
Returns:
A shallow copy of the prototype.
"""
return copy.copy(self)
def deep_clone(self) -> Prototype:
"""Create a deep copy of the object.
Returns:
A deep copy of the prototype.
"""
return copy.deepcopy(self)
class Document(Prototype):
"""Represents a document that can be cloned.
This demonstrates the prototype pattern with both shallow and deep copying.
"""
def __init__(self, title: str, font: str, font_size: int) -> None:
"""Initialize a document.
Args:
title: The document title.
font: The font name.
font_size: The font size.
"""
self.title = title
self.font = font
self.font_size = font_size
self.sections: list[str] = []
self.metadata: dict[str, Any] = {}
def add_section(self, section: str) -> None:
"""Add a section to the document.
Args:
section: The section name or content.
"""
self.sections.append(section)
def set_metadata(self, key: str, value: Any) -> None:
"""Set document metadata.
Args:
key: The metadata key.
value: The metadata value.
"""
self.metadata[key] = value
def get_info(self) -> str:
"""Get document information.
Returns:
A string describing the document.
"""
return (f"Document: {self.title}, "
f"Font: {self.font} {self.font_size}pt, "
f"Sections: {len(self.sections)}")
Usage¶
# Create original document
original = Document("Report", "Arial", 12)
original.add_section("Introduction")
original.add_section("Methodology")
original.set_metadata("author", "John Doe")
# Shallow copy - shares mutable references
shallow_copy = original.clone()
shallow_copy.title = "Modified Report"
shallow_copy.add_section("Results") # Affects original too!
# Deep copy - independent copy
deep_copy = original.deep_clone()
deep_copy.title = "Independent Report"
deep_copy.add_section("Conclusion") # Does NOT affect original
print(f"Original sections: {len(original.sections)}") # 3
print(f"Deep copy sections: {len(deep_copy.sections)}") # 4
Trade-offs¶
Benefits:
- Cloning can be faster than creating objects from scratch
- Add or remove prototypes at runtime for flexibility
- Avoid factory hierarchies for product variants
- Clone objects in specific configured states
Drawbacks:
- Managing shallow vs deep copy semantics can be tricky
- Objects with circular references are hard to clone
- Implementing proper cloning can be complex
- Cloned objects may need additional initialization
Real-World Examples¶
- Document templates with pre-configured settings
- Game objects with preset configurations (enemies, weapons, items)
- Graphics editors cloning shapes or design elements
- Test fixtures cloning test data objects
Related Patterns¶
- Abstract Factory
- Composite
- Decorator
- Memento
- Singleton
API Reference¶
design_patterns.creational.prototype
¶
Prototype Pattern Module
The Prototype pattern creates new objects by cloning existing instances rather than creating new ones from scratch. This is useful when object creation is expensive or when you want to avoid the complexity of instantiating an object directly.
Python provides built-in support for prototyping through the copy module, which
offers both shallow and deep copying mechanisms.
Example
Cloning a document with its formatting:
Circle
¶
Bases: Shape
Represents a circle shape.
Source code in src/design_patterns/creational/prototype.py
__init__(x, y, color, radius)
¶
Initialize a circle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
int
|
X coordinate of center. |
required |
y
|
int
|
Y coordinate of center. |
required |
color
|
str
|
Circle color. |
required |
radius
|
int
|
Circle radius. |
required |
Source code in src/design_patterns/creational/prototype.py
__repr__()
¶
Return string representation of the circle.
Returns:
| Type | Description |
|---|---|
str
|
String representation. |
Document
¶
Bases: Prototype
Represents a document that can be cloned.
This demonstrates the prototype pattern with both shallow and deep copying.
Source code in src/design_patterns/creational/prototype.py
__init__(title, font, font_size)
¶
Initialize a document.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
title
|
str
|
The document title. |
required |
font
|
str
|
The font name. |
required |
font_size
|
int
|
The font size. |
required |
Source code in src/design_patterns/creational/prototype.py
add_section(section)
¶
Add a section to the document.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
section
|
str
|
The section name or content. |
required |
get_info()
¶
Get document information.
Returns:
| Type | Description |
|---|---|
str
|
A string describing the document. |
Source code in src/design_patterns/creational/prototype.py
set_metadata(key, value)
¶
Set document metadata.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
The metadata key. |
required |
value
|
Any
|
The metadata value. |
required |
Prototype
¶
Abstract base class for prototypes.
Defines the interface for cloning objects.
Source code in src/design_patterns/creational/prototype.py
PrototypeRegistry
¶
Registry for managing prototype instances.
This allows storing and retrieving prototype objects by name, which can then be cloned to create new instances.
Source code in src/design_patterns/creational/prototype.py
__init__()
¶
clone(name)
¶
Clone a registered prototype.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The name of the prototype to clone. |
required |
Returns:
| Type | Description |
|---|---|
Prototype
|
A shallow copy of the registered prototype. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If the prototype name is not registered. |
Source code in src/design_patterns/creational/prototype.py
deep_clone(name)
¶
Deep clone a registered prototype.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The name of the prototype to clone. |
required |
Returns:
| Type | Description |
|---|---|
Prototype
|
A deep copy of the registered prototype. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If the prototype name is not registered. |
Source code in src/design_patterns/creational/prototype.py
register(name, prototype)
¶
Register a prototype with a given name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The name to register the prototype under. |
required |
prototype
|
Prototype
|
The prototype object to register. |
required |
Source code in src/design_patterns/creational/prototype.py
unregister(name)
¶
Unregister a prototype.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The name of the prototype to unregister. |
required |
Rectangle
¶
Bases: Shape
Represents a rectangle shape.
Source code in src/design_patterns/creational/prototype.py
__init__(x, y, color, width, height)
¶
Initialize a rectangle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
int
|
X coordinate of top-left corner. |
required |
y
|
int
|
Y coordinate of top-left corner. |
required |
color
|
str
|
Rectangle color. |
required |
width
|
int
|
Rectangle width. |
required |
height
|
int
|
Rectangle height. |
required |
Source code in src/design_patterns/creational/prototype.py
__repr__()
¶
Return string representation of the rectangle.
Returns:
| Type | Description |
|---|---|
str
|
String representation. |
Source code in src/design_patterns/creational/prototype.py
Shape
¶
Bases: Prototype
Represents a geometric shape that can be cloned.
This demonstrates cloning with position and style attributes.
Source code in src/design_patterns/creational/prototype.py
__init__(x, y, color)
¶
Initialize a shape.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
int
|
X coordinate. |
required |
y
|
int
|
Y coordinate. |
required |
color
|
str
|
Shape color. |
required |
__repr__()
¶
Return string representation of the shape.
Returns:
| Type | Description |
|---|---|
str
|
String representation. |
move(dx, dy)
¶
Move the shape by the given deltas.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dx
|
int
|
Change in x coordinate. |
required |
dy
|
int
|
Change in y coordinate. |
required |