Mastering Design Patterns in C# and ASP.NET Core
Part 2.5 – Prototype Design Pattern
Series: Design Patterns in C# and ASP.NET Core
Pattern Category: Creational Design Pattern
Difficulty: ⭐⭐⭐☆☆ (Intermediate)
Prerequisites: OOP Concepts, Classes, Reference Types, Object Creation
Table of Contents
- Introduction
- What is the Prototype Pattern?
- Why Do We Need the Prototype Pattern?
- Problem Statement
- Real-World Analogy
- Prototype Pattern Structure
- UML Class Diagram
- Shallow Copy vs Deep Copy
- MemberwiseClone() in C#
- ICloneable Interface
- Custom Deep Cloning
- Complete C# Console Application
- ASP.NET Core Example
- Real-World Use Cases
- Advantages
- Disadvantages
- Best Practices
- Common Mistakes
- Interview Questions
- Summary
Introduction
In software development, creating objects is usually straightforward:
Employee employee = new Employee();
However, some objects are expensive to create.
Examples:
- Large configuration objects
- Complex report templates
- Game characters
- Machine learning models
- Document templates
- UI layouts
Sometimes creating a new object requires:
- Database calls
- API calls
- Complex calculations
- Heavy initialization
Instead of creating a brand-new object every time, we can copy an existing object and modify only the necessary properties.
This is exactly what the Prototype Design Pattern provides.
What is the Prototype Pattern?
Definition
The Prototype Pattern is a creational design pattern that creates new objects by copying an existing object (prototype) instead of creating them from scratch.
Rather than building a new object using constructors, the application clones an existing instance.
Why Do We Need the Prototype Pattern?
Imagine a report object that takes several seconds to initialize.
Report report = new Report();
report.LoadData();
report.GenerateCharts();
report.ApplyFormatting();
Creating hundreds of reports this way is expensive.
Instead:
Report reportTemplate = LoadReportTemplate();
Report report1 = reportTemplate.Clone();
Report report2 = reportTemplate.Clone();
Report report3 = reportTemplate.Clone();
The heavy initialization occurs only once.
Problem Statement
Suppose you're creating employee onboarding profiles.
Employee employee = new Employee
{
Department = "IT",
Country = "India",
Company = "ABC Technologies"
};
Most employees share these values.
Creating each object repeatedly introduces duplication.
The Prototype Pattern allows copying a template and changing only what differs.
Real-World Analogy
Imagine a photocopy machine.
You create one original document.
Instead of rewriting the same document 100 times, you simply make copies.
Original Document
|
v
Photocopy Machine
|
+---- Copy 1
+---- Copy 2
+---- Copy 3
The original document acts as the prototype.
Prototype Pattern Structure
The pattern consists of:
- Prototype Interface
- Concrete Prototype
- Client
The client requests a clone from the prototype instead of constructing a new object.
UML Class Diagram
+----------------------+
| IPrototype |
+----------------------+
| + Clone() |
+----------^-----------+
|
+----------------------+
| Employee |
+----------------------+
| Name |
| Department |
| Salary |
+----------------------+
| Clone() |
+----------------------+
^
|
Client
Shallow Copy vs Deep Copy
Understanding these concepts is critical.
Shallow Copy
Copies:
- Value type fields
- References only for reference-type objects
Example
public class Address
{
public string City { get; set; }
}
public class Employee
{
public string Name { get; set; }
public Address Address { get; set; }
}
If a shallow copy is created:
Employee A
|
+---- Address
Employee B
|
+---- Same Address
Both employees share the same address object.
Changes affect both objects.
Deep Copy
Creates completely independent copies.
Employee A
|
+---- Address A
Employee B
|
+---- Address B
Changes to one object do not affect the other.
MemberwiseClone() in C#
The .NET Framework provides:
MemberwiseClone()
This method creates a shallow copy.
Example:
public class Employee
{
public string Name { get; set; }
public Employee Clone()
{
return (Employee)this.MemberwiseClone();
}
}
Usage:
Employee emp1 = new Employee
{
Name = "John"
};
Employee emp2 = emp1.Clone();
This is the simplest prototype implementation.
ICloneable Interface
C# provides:
public interface ICloneable
{
object Clone();
}
Implementation:
public class Employee : ICloneable
{
public string Name { get; set; }
public object Clone()
{
return MemberwiseClone();
}
}
Usage:
Employee emp2 = (Employee)emp1.Clone();
Limitation of ICloneable
Microsoft does not specify whether:
Clone()
returns:
- Shallow copy
or - Deep copy
For this reason, many modern applications prefer custom cloning methods with clear behavior.
Custom Deep Cloning
Consider:
public class Address
{
public string City { get; set; }
}
public class Employee
{
public string Name { get; set; }
public Address Address { get; set; }
public Employee DeepClone()
{
return new Employee
{
Name = this.Name,
Address = new Address
{
City = this.Address.City
}
};
}
}
Now every clone has its own address object.
Complete C# Console Application
Employee
public class Employee
{
public string Name { get; set; }
public string Department { get; set; }
public decimal Salary { get; set; }
public Employee Clone()
{
return (Employee)this.MemberwiseClone();
}
}
Program
class Program
{
static void Main()
{
Employee employee1 = new Employee
{
Name = "John",
Department = "IT",
Salary = 75000
};
Employee employee2 = employee1.Clone();
employee2.Name = "David";
Console.WriteLine(employee1.Name);
Console.WriteLine(employee2.Name);
}
}
Output
John
David
The cloned object can be modified independently.
ASP.NET Core Example
Suppose an application creates invoice templates.
public class InvoiceTemplate
{
public string CompanyName { get; set; }
public string FooterText { get; set; }
public InvoiceTemplate Clone()
{
return (InvoiceTemplate)this.MemberwiseClone();
}
}
Service:
public class InvoiceService
{
private readonly InvoiceTemplate _template;
public InvoiceService()
{
_template = new InvoiceTemplate
{
CompanyName = "ABC Technologies",
FooterText = "Thank You"
};
}
public InvoiceTemplate CreateInvoice()
{
return _template.Clone();
}
}
Benefits:
- Template initialized once.
- New invoices generated quickly.
- Reduced setup cost.
Real-World Use Cases
The Prototype Pattern is useful when object creation is expensive or repetitive.
Examples:
Document Templates
Invoice Template
Resume Template
Contract Template
Game Development
Enemy Templates
Characters
Weapons
Vehicles
UI Frameworks
Forms
Controls
Dashboards
Widgets
Reporting Systems
Monthly Reports
Sales Reports
Audit Reports
Configuration Objects
Database Configuration
Cloud Configuration
Application Settings
Workflow Systems
Approval Workflow
Review Workflow
Deployment Workflow
Advantages
Faster Object Creation
Heavy initialization occurs only once.
Reduced Resource Consumption
Avoids repeated calculations and setup operations.
Simplifies Complex Object Creation
Clone existing objects rather than reconstructing them.
Supports Runtime Object Creation
Objects can be copied dynamically.
Reduces Duplicate Code
Reuse preconfigured templates.
Disadvantages
Deep Cloning Can Be Complex
Objects with many nested references require careful copying.
Circular References
Can complicate cloning logic.
Hidden Dependencies
Developers may not realize cloned objects share references.
Maintenance Overhead
Custom deep-copy implementations must be updated when new fields are added.
Best Practices
Clearly Define Clone Behavior
Specify whether cloning is shallow or deep.
Prefer Deep Cloning for Mutable Objects
Prevents unexpected side effects.
Use Prototypes for Expensive Objects
Avoid unnecessary cloning for lightweight objects.
Keep Cloning Logic Centralized
Place cloning behavior inside the object itself or a dedicated cloning service.
Test Clones Thoroughly
Verify that modifications do not leak across copies.
Common Mistakes
Assuming MemberwiseClone Creates a Deep Copy
Incorrect:
Employee clone = original.Clone();
Nested reference objects are still shared.
Forgetting Nested Objects
Address
Department
Manager
Must also be copied for a true deep clone.
Using Prototype for Simple Objects
Creating:
new Customer();
is often simpler than cloning.
Ignoring Performance Trade-Offs
Deep cloning very large object graphs can be expensive.
Interview Questions
1. What is the Prototype Pattern?
A creational design pattern that creates new objects by cloning existing objects instead of constructing them from scratch.
2. When should the Prototype Pattern be used?
When:
- Object creation is expensive.
- Similar objects are frequently required.
- Initialization is complex.
- Templates are reused.
3. What is the difference between shallow copy and deep copy?
| Shallow Copy | Deep Copy |
|---|---|
| Copies references | Copies actual objects |
| Faster | Slower |
| Shared nested objects | Independent nested objects |
| Higher risk of side effects | Safer |
4. What does MemberwiseClone() do?
Creates a shallow copy of the current object.
5. Why is ICloneable controversial?
Because it does not specify whether cloning should be shallow or deep, leading to ambiguity.
6. Give real-world examples of Prototype Pattern.
- Invoice templates
- Resume templates
- Game character templates
- Dashboard widgets
- Report templates
- Configuration objects
7. Prototype vs Builder Pattern?
| Prototype | Builder |
|---|---|
| Copies existing object | Creates object step-by-step |
| Focuses on cloning | Focuses on construction |
| Fast duplication | Flexible configuration |
| Good for templates | Good for complex objects |
Summary
The Prototype Design Pattern is a powerful creational pattern that creates objects by cloning existing instances rather than constructing them from scratch. It is especially useful when object initialization is expensive, repetitive, or based on reusable templates.
Understanding the difference between shallow copy and deep copy is essential for implementing the pattern correctly. While MemberwiseClone() provides a quick way to create shallow copies, enterprise applications often require custom deep-cloning strategies to avoid unintended side effects.
By mastering the Prototype Pattern, you gain another valuable tool for improving performance, reducing duplication, and simplifying object creation in modern C# and ASP.NET Core applications.
Creational Design Patterns Completed 🎉
You have now covered all five GoF Creational Design Patterns:
- Singleton Pattern
- Factory Method Pattern
- Abstract Factory Pattern
- Builder Pattern
- Prototype Pattern
Coming Up Next: Part 3 – Structural Design Patterns
We'll explore:
- Adapter Pattern
- Bridge Pattern
- Composite Pattern
- Decorator Pattern
- Facade Pattern
- Flyweight Pattern
- Proxy Pattern
These patterns focus on how classes and objects are composed to form larger, more flexible structures in enterprise applications.
No comments:
Post a Comment