// Design Patterns · 04
Prototype
Create new objects by copying a configured instance.
How it fits together
- ReportRunner uses / calls TemplateRegistry (Get(name))
- TemplateRegistry wraps / holds ReportTemplate (holds)
- SalesReport extends ReportTemplate
- StockReport extends ReportTemplate
- SalesReport creates SalesReport copy (Clone())
- ReportRunner uses / calls SalesReport copy (edits)
Spot it
- Building an object needs a slow lookup or parse
- Many near-identical objects set up by hand
- Callers must know the concrete type to copy it
It costs
Deep copies are easy to get wrong, and shared references leak changes.
In code
public sealed record Section(string Title, IReadOnlyList<string> Filters);public abstract class ReportTemplate{ public List<Section> Sections { get; init; } = []; public abstract ReportTemplate Clone();}public sealed class SalesReport : ReportTemplate{ public string Currency { get; set; } = "EUR"; public override ReportTemplate Clone() => new SalesReport { Currency = Currency, Sections = Sections .Select(section => section with { }) .ToList(), };}public sealed class TemplateRegistry(IReadOnlyDictionary<string, ReportTemplate> templates){ public ReportTemplate Get(string name) => templates[name].Clone();}Go deeperThe full explanation and what to ask for · 1 min readExpandCollapse
A prototype is an object that knows how to copy itself. Callers ask an existing, configured instance for a clone instead of calling a constructor, so they need neither the concrete type nor the setup steps.
The trap is the shallow copy: the clone shares lists or nested objects with the original, and editing one silently edits the other. Decide per field whether to deep-copy, and test it. Objects that hold connections, handles or an identity should not be cloned at all.
Many languages offer a cheap form of this: copy-with-changes on immutable values, or a copy constructor. A registry of prototypes behaves like a factory keyed by name, and an Abstract Factory can clone prototypes instead of constructing products.
Ask for it
- prototype
- clone
- shallow copy
- deep copy
- prototype registry
Report templates are rebuilt from the database for every run. Load each ReportTemplate once into a TemplateRegistry, give ReportTemplate a Clone() that deep-copies its sections and filters, and have runs clone a template before changing it. Test that editing a clone leaves the original unchanged.