Creational Patterns#
flowchart LR
C[Caller]
F([Factory / Builder / Singleton])
P[Product]
C -->|create / get| F --> P
classDef p fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef s fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef r fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
class C p;
class F s;
class P r;
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class C,F,P service;
Five patterns for hiding how an object gets constructed: Singleton, Factory Method, Abstract Factory, Builder, Prototype.
Singleton#
Single instance, accessed via a static method.
classDiagram
class Singleton {
-instance: Singleton
-Singleton()
+getInstance() Singleton
+operation()
}
Singleton --> Singleton : returns self
class Logger {
private static volatile Logger INSTANCE;
private Logger() {}
public static Logger getInstance() {
if (INSTANCE == null) synchronized (Logger.class) {
if (INSTANCE == null) INSTANCE = new Logger();
}
return INSTANCE;
}
}
Watch: hidden global state, breaks testability. Most languages prefer a single instance passed via DI rather than a true singleton.
Factory Method#
A method on a base class decides which concrete to instantiate.
classDiagram
class Creator {
<<abstract>>
+createProduct() Product
+operation()
}
class ConcreteCreatorA {
+createProduct() Product
}
class ConcreteCreatorB {
+createProduct() Product
}
class Product
class ConcreteProductA
class ConcreteProductB
Creator <|-- ConcreteCreatorA
Creator <|-- ConcreteCreatorB
Product <|-- ConcreteProductA
Product <|-- ConcreteProductB
ConcreteCreatorA ..> ConcreteProductA
ConcreteCreatorB ..> ConcreteProductB
Use when the caller shouldn't know which concrete type comes back.
Abstract Factory#
A factory that returns families of related products (e.g. a UI toolkit's Button + Checkbox + Menu).
classDiagram
class GUIFactory {
<<interface>>
+createButton() Button
+createCheckbox() Checkbox
}
class MacFactory
class WinFactory
GUIFactory <|-- MacFactory
GUIFactory <|-- WinFactory
class Button
class Checkbox
class MacButton
class WinButton
Button <|-- MacButton
Button <|-- WinButton
Builder#
Construct a complex object step-by-step. Most useful when an object has many optional parameters.
Pizza p = new Pizza.Builder()
.size(LARGE)
.addTopping("cheese")
.addTopping("olives")
.crust("thin")
.build();
Java records / Kotlin data classes / Python kwargs replace some of this, but the fluent-builder is still common.
Prototype#
Clone an existing instance instead of constructing a new one. Useful when construction is expensive.
classDiagram
class Prototype {
<<interface>>
+clone() Prototype
}
class Page {
-title
-content
+clone() Page
}
Prototype <|.. Page
Choosing among them#
| Use | Pick |
|---|---|
| Hide one type's construction | Factory Method |
| Hide a family of types together | Abstract Factory |
| Many optional / required fields | Builder |
| Construction is expensive; you want copies | Prototype |
| You truly need exactly one instance | Singleton (sparingly) |
Where these show up in this site#
- Logger framework uses Singleton for the root logger + Factory for appenders.
- Vending machine / ATM / Hotel management use Builder for complex requests.
- Chess engine uses Prototype for memento/undo snapshots.
Glossary & fundamentals#
Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.
| Tag | Concept | What it is | Page |
|---|---|---|---|
HLD |
Observability | metrics, logs, traces, SLOs | observability |
LLD |
Creational patterns | Singleton, Factory, Builder, Prototype | creational-patterns |
LLD |
Behavioural patterns | Strategy, Observer, State, Command, Chain | behavioral-patterns |
LLD |
Dependency injection | DI / IoC, constructor injection, containers | dependency-injection |
Quick reference#
Why#
- Decouple "what to use" from "how to make it".
- Centralise construction logic so it can be swapped, tested, mocked.
Singleton pitfalls#
- Acts as global state.
- Hard to test (must reset between tests).
- Multi-threaded init needs care (double-checked locking,
volatile, or eager init). - Frequently better: inject a single instance via the DI container.
Builder vs telescoping constructors#
-
4 parameters? Reach for Builder.
- Required vs optional: enforce required in
Builderconstructor; expose setters for optional. - Fluent (return
this) reads better.
Modern alternatives#
- Kotlin:
data class+ named/default args replaces Builder for many cases. - Java records (since 14) cover simple POJOs.
- Python: keyword arguments +
dataclassesreplace most of Builder. - Go: functional options (
WithTimeout(5),WithRetries(3)).
Refs#
- Design Patterns: Elements of Reusable Object-Oriented Software - Gamma, Helm, Johnson, Vlissides (GoF, 1994).
- Effective Java (Bloch) - Item 2 (Builder), Item 3 (Singleton).
- Refactoring.guru patterns gallery.
FAQ#
What are creational design patterns?#
Patterns that abstract the process of creating objects so callers do not depend on concrete classes. They include Singleton, Factory Method, Abstract Factory, Builder, and Prototype.
When should I use Singleton?#
For genuinely one-instance resources like a metrics registry or process-wide cache. Avoid it for service dependencies because Singletons hide coupling and make unit testing harder.
Factory method vs abstract factory?#
Factory method picks one concrete product based on a parameter. Abstract factory creates a family of related products that work together, like a UI toolkit with matching buttons and menus.
When should I prefer Prototype over Builder?#
Prototype clones an existing instance, which is handy when construction is expensive or the source already has the right state. Builder constructs from scratch with full control over each field.
Are creational patterns still relevant in modern code?#
Yes, but often expressed via dependency injection containers and records or data classes. The underlying ideas, hiding construction details and decoupling callers, remain useful.
Related Topics#
- Structural Patterns: structural patterns frequently combine with creational ones at the composition boundary
- Behavioral Patterns: creational patterns supply objects that behavioral patterns operate on
- SOLID Principles: DIP and OCP are the primary design forces behind Factory and Abstract Factory
Further reading#
Curated, high-credibility sources for going deeper on this topic.