Builder constructs a complex object step-by-step, which matters most when that object has many optional parameters — the alternative being either a constructor with a dozen parameters (unreadable at the call site, and easy to pass arguments in the wrong order) or a dozen overloads (a combinatorial mess called the "telescoping constructor" problem).
public class Pizza
{
public string Size { get; set; } = "medium";
public bool ExtraCheese { get; set; }
public bool Pepperoni { get; set; }
public bool Mushrooms { get; set; }
}
public class PizzaBuilder
{
private readonly Pizza _pizza = new();
public PizzaBuilder WithSize(string size) { _pizza.Size = size; return this; }
public PizzaBuilder AddExtraCheese() { _pizza.ExtraCheese = true; return this; }
public PizzaBuilder AddPepperoni() { _pizza.Pepperoni = true; return this; }
public PizzaBuilder AddMushrooms() { _pizza.Mushrooms = true; return this; }
public Pizza Build() => _pizza;
}
// Readable at the call site, in any order, with only the options that matter named explicitly.
var pizza = new PizzaBuilder()
.WithSize("large")
.AddExtraCheese()
.AddPepperoni()
.Build();
Prototype creates a new object by cloning an existing one, rather than constructing it from scratch — useful when building an object from raw inputs is expensive (an expensive database lookup, a computed configuration) but a small variation of an already-built object is what's actually needed.
public class DocumentTemplate : ICloneable
{
public string Header { get; set; } = "";
public string Footer { get; set; } = "";
public List<string> Sections { get; set; } = new();
public object Clone()
{
// A shallow Clone() is not always enough — Sections is a reference type,
// so both the original and the clone would share the same List instance
// unless it's copied explicitly, as done here.
return new DocumentTemplate
{
Header = Header,
Footer = Footer,
Sections = new List<string>(Sections)
};
}
}
var baseTemplate = new DocumentTemplate { Header = "Company Report", Footer = "Confidential" };
var invoiceVariant = (DocumentTemplate)baseTemplate.Clone();
invoiceVariant.Sections.Add("Invoice details"); // doesn't affect baseTemplate.Sections
That comment about shallow versus deep cloning is the real gotcha with Prototype in any language with reference types: a naive member-wise clone copies references to nested objects, not the objects themselves, so mutating a "cloned" collection can silently mutate the original too unless the clone logic explicitly copies nested reference-type fields.