Prototype Design Pattern Simplified
Simplifying Object Cloning with Prototype Patterns in C#
The Prototype Pattern is a creational design pattern that allows you to create new objects by copying (cloning) existing objects, rather than creating new instances from scratch.
Instead of using
newto create an object, you clone an existing object and modify it if needed.It is useful when object creation is costly or complex.
Key idea:
“If creating an object is expensive, make a copy of a prototype instead.”
Why the Prototype Pattern Is Useful
In many software applications, you often need to create objects that are similar but not identical. For example:
A game creating multiple enemy characters with shared stats
A GUI application generating windows or controls with the same style
Business software producing reports, invoices, or contracts that share structure
Instead of repeatedly writing code to build each object from scratch, the Prototype Pattern allows you to:
Create a template object once
Clone it whenever you need a new instance
Modify only the parts that differ
This leads to simpler, cleaner, and more maintainable code.
Example: Enterprise Document Generation
One practical example of the Prototype Pattern is generating enterprise documents like invoices, quotes, or reports.
These documents often share:
Headers, footers, and logos
Layouts and formatting
Calculation logic (totals, taxes)
Yet each document also has unique details like:
Customer data
Dates and IDs
Line items
Using a prototype allows you to define a document template once and then clone it for each new document, only changing the necessary details.
Code Example
1. Line Item (changes per invoice)
public sealed class LineItem
{
public string Description { get; set; } = "";
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public decimal Total => Quantity * UnitPrice;
public LineItem Clone() => new()
{
Description = this.Description,
Quantity = this.Quantity,
UnitPrice = this.UnitPrice
};
}
2. Header & Footer (shared structure)
public sealed class DocumentHeader
{
public string CompanyName { get; init; } = "";
public string Address { get; init; } = "";
public string LogoPath { get; init; } = "";
public DocumentHeader Clone() => this with { };
}
public sealed class DocumentFooter
{
public string LegalText { get; init; } = "";
public string BankDetails { get; init; } = "";
public DocumentFooter Clone() => this with { };
}
3. Prototype Interface
public interface IDocumentPrototype<T>
{
T Clone();
decimal CalculateTotal();
}
4. Invoice Template (Prototype)
public sealed class InvoiceTemplate : IDocumentPrototype<InvoiceTemplate>
{
// Shared template structure
public DocumentHeader Header { get; init; } = new();
public DocumentFooter Footer { get; init; } = new();
public decimal TaxRate { get; init; } = 0.20m;
// Instance-specific data
public string InvoiceNumber { get; set; } = "";
public DateTime IssueDate { get; set; } = DateTime.Today;
public List<LineItem> LineItems { get; private set; } = new();
public decimal CalculateTotal()
{
var subtotal = LineItems.Sum(i => i.Total);
return subtotal * (1 + TaxRate);
}
public InvoiceTemplate Clone()
{
return new InvoiceTemplate
{
Header = Header.Clone(),
Footer = Footer.Clone(),
TaxRate = TaxRate,
InvoiceNumber = "",
IssueDate = DateTime.Today,
LineItems = new List<LineItem>()
};
}
}
5. Load Template Once (Factory)
public static class InvoiceTemplateFactory
{
public static InvoiceTemplate LoadTemplate()
{
return new InvoiceTemplate
{
Header = new DocumentHeader
{
CompanyName = "Acme Corp",
Address = "123 Business Street",
LogoPath = "/logos/acme.png"
},
Footer = new DocumentFooter
{
LegalText = "Payment due within 30 days.",
BankDetails = "IBAN XX00 0000 0000"
},
TaxRate = 0.20m
};
}
}
6. Document Service: Create Real Invoices
public sealed class DocumentService
{
private readonly InvoiceTemplate _invoicePrototype;
public DocumentService()
{
// Load the template once at startup
_invoicePrototype = InvoiceTemplateFactory.LoadTemplate();
}
public InvoiceTemplate CreateInvoice()
{
// Clone the template
var invoice = _invoicePrototype.Clone();
// Assign unique details
invoice.InvoiceNumber = $"INV-{DateTime.Now:yyyyMMdd}-{Guid.NewGuid().ToString("N").Substring(0, 6)}";
invoice.IssueDate = DateTime.Today;
// Add line items
invoice.LineItems.Add(new LineItem
{
Description = "Website redesign",
Quantity = 1,
UnitPrice = 4500m
});
invoice.LineItems.Add(new LineItem
{
Description = "SEO optimization (6 months)",
Quantity = 1,
UnitPrice = 1800m
});
return invoice;
}
}
7. Usage Example
var documentService = new DocumentService();
// Create a new invoice
var invoice = documentService.CreateInvoice();
Console.WriteLine($"Invoice Number: {invoice.InvoiceNumber}");
Console.WriteLine($"Issue Date: {invoice.IssueDate:yyyy-MM-dd}");
Console.WriteLine("Items:");
foreach (var item in invoice.LineItems)
{
Console.WriteLine($"- {item.Description}: {item.Quantity} x {item.UnitPrice:C} = {item.Total:C}");
}
Console.WriteLine($"Total (with tax): {invoice.CalculateTotal():C}");
The Prototype Pattern is a simple yet powerful tool for creating new objects by cloning existing ones. It reduces repetitive code, improves performance, and ensures consistency across similar objects.
While this pattern is often used in enterprise scenarios like document generation, its benefits apply broadly—from games and UI components to complex business objects. By defining a template once and cloning it whenever needed, developers can save time, reduce errors, and maintain clean, maintainable code.