Discover the Prototype Design Pattern - a creational pattern that creates new objects by copying from prototype instances, saving initialization costs and time in object-oriented programming.
No table of contents available for this article
Prototype Design Pattern is a creational design pattern that deals with object creation in a smart and flexible manner.
Simply put, Prototype allows you to create new objects by cloning existing prototype objects instead of instantiating from scratch using the new keyword. This pattern is particularly useful when:
Object initialization is time-consuming and resource-intensive
You already have a similar object available
You want to avoid dependencies on concrete classes
Usage Frequency: Medium
Real-world Example: Imagine you're designing a game with many types of monsters. Instead of creating each monster from scratch (with all properties like HP, damage, skills...), you can clone from a "prototype monster" and only modify a few necessary attributes.
Suppose you want to create an exact copy of an existing object. The conventional approach would be:
Create a new object of the same class
Iterate through all fields of the original object
Copy each value to the new object
However, this approach faces 2 serious issues:
Issue 1 - Private Fields: Some attributes might be private or protected, inaccessible from outside the class.
Issue 2 - Class Dependency: Your code must know the exact class of the object to clone, creating tight coupling.
Example:
csharp
// WRONG approach - Dependent on concrete class
public Person CopyPerson(Person original) {
Person copy = new Person(); // Must know Person class
copy.Name = original.Name;
copy.Age = original.Age;
// Can't copy private fields!
return copy;
}Prototype Pattern solves this by delegating the cloning process to the actual objects being cloned.
How it works:
Create a common interface (typically ICloneable or IPrototype) with a Clone() method
Each class implements this interface and defines its own cloning logic
Client code just calls Clone() without knowing the concrete class
Benefits:
Objects can clone themselves, accessing private fields
Client code is independent of concrete classes
Can clone complex objects with nested structures
Main Components:
1. Prototype (Interface): Declares the Clone() method for object copying.
2. ConcretePrototype (Concrete Class): Implements the Clone() method to perform copying.
3. Client: Creates new objects by calling the Clone() method on prototypes.
Two Types of Cloning:
Shallow Copy:
Only copies value types (int, float, bool...)
Reference types only copy the address (pointing to the same object)
Fast and simple, but may cause side effects
Deep Copy:
Copies everything, including nested objects
New object is completely independent from original
Slower but safer
1. Add/Remove Concrete Classes at Runtime
Much more flexible than Factory Pattern
Can register or remove prototypes while program is running
2. Reduce Number of Subclasses
No need to create numerous subclasses like Abstract Factory
Reduces complexity of class hierarchy
3. Save Initialization Costs
Cloning is much faster than initialization from scratch with new
Especially effective with complex objects
4. Flexible Object Configuration
Can create "prototype objects" with various configurations
Clone and modify a few attributes instead of configuring from scratch
5. Easier Complex Object Creation
Good support for hierarchical systems (tree, composite)
Can clone entire complex structures with multiple layers
1. Difficult with Circular References
Objects with circular references (A → B → A) are very hard to clone
Requires special handling to avoid infinite loops
2. Must Implement Clone() for Every Class
Every subclass must override the Clone() method
Can be tedious when system has many classes
3. Difficulty with Non-Cloneable Objects
Some objects can't be cloned (database connections, file handles...)
Requires special handling for each case
Use When:
High Initialization Cost: When creating objects from scratch is time/resource intensive (database queries, loading large files, complex calculations...)
Similar Object Available: When you already have an object similar to what you need
Want Class Independence: When you want to avoid direct dependency on concrete classes
Need Many Variants: When you need many objects with slightly different configurations
Don't Use When:
Objects are simple with fast initialization
No prototype available
Objects have many circular dependencies
Subclasses differ too much in structure
Real-World Use Cases:
Graphics Applications: Clone shapes, images with similar configurations
Game Development: Clone enemies, items, characters
CAD/GIS Systems: Clone complex design objects
Document Editors: Clone styles, templates, formatting
Configuration Management: Clone settings with minor modifications
Here's a detailed example of both Shallow Copy and Deep Copy:
csharp
// Class containing ID information
public class IdInfo
{
public int IdNumber;
public IdInfo(int idNumber)
{
this.IdNumber = idNumber;
}
}
// Person class with 2 clone methods
public class Person
{
// Value types
public int Age;
public DateTime BirthDate;
public string Name;
// Reference type
public IdInfo IdInfo;
// SHALLOW COPY: Only copies value types
public Person ShallowCopy()
{
// MemberwiseClone() is a built-in method in C#
return (Person)this.MemberwiseClone();
}
// DEEP COPY: Copies reference types too
public Person DeepCopy()
{
Person clone = (Person)this.MemberwiseClone();
// Create new IdInfo object instead of copying reference
clone.IdInfo = new IdInfo(IdInfo.IdNumber);
// String in C# is immutable, so use String.Copy
clone.Name = String.Copy(Name);
return clone;
}
}
// Demo program
class Program
{
static void Main(string[] args)
{
// Create original object
Person p1 = new Person();
p1.Age = 42;
p1.BirthDate = Convert.ToDateTime("1977-01-01");
p1.Name = "Jack Daniels";
p1.IdInfo = new IdInfo(666);
// Shallow copy
Person p2 = p1.ShallowCopy();
// Deep copy
Person p3 = p1.DeepCopy();
// Display initial values
Console.WriteLine("=== Initial Values ===");
Console.WriteLine("P1:"); DisplayValues(p1);
Console.WriteLine("P2 (Shallow):"); DisplayValues(p2);
Console.WriteLine("P3 (Deep):"); DisplayValues(p3);
// Modify p1
Console.WriteLine("\n=== After Modifying P1 ===");
p1.Age = 32;
p1.BirthDate = Convert.ToDateTime("1900-01-01");
p1.Name = "Frank";
p1.IdInfo.IdNumber = 7878;
Console.WriteLine("P1:"); DisplayValues(p1);
// P2: Reference types ARE AFFECTED (Shallow Copy)
Console.WriteLine("P2 (Shallow - IdInfo changed):");
DisplayValues(p2);
// P3: NOT AFFECTED (Deep Copy)
Console.WriteLine("P3 (Deep - unchanged):");
DisplayValues(p3);
}
public static void DisplayValues(Person p)
{
Console.WriteLine($" Name: {p.Name}, Age: {p.Age}");
Console.WriteLine($" BirthDate: {p.BirthDate:MM/dd/yy}");
Console.WriteLine($" ID: {p.IdInfo.IdNumber}\n");
}
}Result Explanation:
P2 (Shallow Copy): IdInfo still points to the original object, so when p1 changes IdNumber, p2 is also affected
P3 (Deep Copy): Has its own IdInfo object, so completely independent
Common Pattern Combinations:
1. Factory Method and Prototype
Many designs start with Factory Method (simpler)
Then evolve to Prototype when more flexibility is needed
2. Abstract Factory combined with Prototype
Abstract Factory can use Prototype to create products
Prototype helps reduce necessary subclasses
3. Composite and Prototype
Prototype is very useful for cloning entire Composite trees
Instead of rebuilding from scratch, just clone
4. Decorator and Prototype
Can clone entire decorator chains
Easy to create variants of decorated objects
5. Command Pattern and Prototype
Clone commands to save in history/undo stack
Each command in history is a clone
Comparison with Similar Patterns:
Prototype: Based on Cloning, simple initialization, high flexibility
Factory Method: Based on Inheritance, more complex initialization, medium flexibility
Abstract Factory: Based on Composition, most complex initialization, low flexibility
Important Notes:
Abstract Factories, Builders, and Prototypes can all be implemented as Singletons
Prototype doesn't rely on inheritance, avoiding many inheritance-related issues
Factory Method needs many subclasses; Prototype just needs Clone() implementation
Prototype Design Pattern is a powerful tool when you need to:
Create many similar objects
Save initialization costs
Avoid dependencies on concrete classes
However, be careful with circular references and ensure you choose the right copy type (shallow vs deep) for each specific situation.
Key Takeaways:
Clone instead of new when you have a prototype available
Shallow copy is fast but shares references
Deep copy is slower but completely independent
Suitable for graphics, games, CAD systems
If you found this article helpful, explore more in the Design Patterns Series to enhance your programming skills!
[1] Refactoring.Guru. https://refactoring.guru/design-patterns/prototype
[2] Design Patterns for Dummies, Steve Holzner, PhD
[3] Head First Design Patterns, Eric Freeman
[4] Gang of Four Design Patterns 4.0
[5] Dive into Design Patterns