Prototype Pattern
Create objects from configured prototypes with an explicit policy for copied and shared state.
· Last updated: 2026-10-25 · 5 min read
Overview
Instead of filling in a report preset from scratch, copy one that is already configured and change a few values. That configured original is the prototype.
The Prototype pattern gives an object an operation that copies it. Callers obtain new objects without repeating their internal construction details.
UML
The caller uses IPreset.Copy(). Preset owns the copy policy and allocates a separate mutable Tags list.
History
GoF cataloged Prototype as a creational pattern in Design Patterns in 1994. A prototype specifies the kind of object to create, and copying it produces the new instance.1
Why use it?
Choosing a prototype selects a starting configuration. Keeping the copy policy with the object prevents callers from copying its internal fields themselves.
A new outer object can still share mutable children. Decide which state must be independent and how to handle resources such as file handles that cannot simply be duplicated.
Example
Copy the prototype and change only the new preset name.
using System;
using System.Collections.Generic;
using System.Linq;
var original = new Preset("base", new[] { "draft" });
var copy = (Preset)original.Copy();
copy.Name = "custom";
Console.WriteLine(original.Name + " / " + copy.Name);
// Expose a copy operation so callers do not copy internal fields themselves.
interface IPreset { IPreset Copy(); }
// Create a new mutable tag list so edits to the copy do not affect the prototype.
sealed class Preset(string name, IEnumerable<string> tags) : IPreset
{
public string Name { get; set; } = name;
public List<string> Tags { get; } = new(tags);
public IPreset Copy() => new Preset(Name, Tags);
}
base / customCopy() returns IPreset. This caller knows the copy is a Preset and casts it to edit its name. The prototype name stays unchanged because the edit affects the new object.
An extended example
Add a tag to the copy. Copy() allocates a new list, so the prototype list remains unchanged.
using System;
using System.Collections.Generic;
using System.Linq;
var original = new Preset("base", new[] { "draft" });
var copy = (Preset)original.Copy();
copy.Tags.Add("reviewed");
Console.WriteLine(string.Join(",", original.Tags));
Console.WriteLine(string.Join(",", copy.Tags));
// Expose a copy operation so callers do not copy internal fields themselves.
interface IPreset { IPreset Copy(); }
// Create a new mutable tag list so edits to the copy do not affect the prototype.
sealed class Preset(string name, IEnumerable<string> tags) : IPreset
{
public string Name { get; set; } = name;
public List<string> Tags { get; } = new(tags);
public IPreset Copy() => new Preset(Name, Tags);
}
draft
draft,reviewedThe example copies the list and shares immutable string values. Mutable objects nested inside that list would need their own copying policy if they must be independent. Prototype does not require indiscriminate deep copying.
TypeScript example
The caller chooses a prototype; Preset decides which state to copy. Keeping that policy with Preset avoids changing every caller when the copying rules change.
// Expose a copy operation so callers do not copy internal fields themselves.
interface IPreset { copy(): IPreset; }
class Preset implements IPreset {
readonly tags: string[];
constructor(public name: string, tags: readonly string[]) { this.tags = [...tags]; }
// Create a new mutable tag list so edits to the copy do not affect the prototype.
copy(): Preset { return new Preset(this.name, this.tags); }
}
const original = new Preset("base", ["draft"]);
const copy = original.copy();
copy.name = "custom";
copy.tags.push("reviewed");
console.log(original.name + " / " + copy.name);
console.log(original.tags.join(","));
console.log(copy.tags.join(","));base / custom
draft
draft,reviewedHere copy() returns Preset, so editing its name and tags needs no cast. The list is new while its immutable string values are shared.
Related reading
Each C# example is a separate .NET 10 console program.
Compile the TypeScript examples for ES2022 or later and run the emitted JavaScript with Node.js.
Footnotes
- Gamma, Helm, Johnson, Vlissides. Design Patterns (1994). Publisher record. Creational patterns, Prototype. ↩