Handbooks / OOP & Design Patterns / Chapter 3
Creational Patterns
7 pages · ~5 min✓ Reviewed
Builds on SOLID Principles. Next up: Structural Patterns.
Creational Patterns: The Mental Model
1. The Idea
- Decouple what gets created from the code that asks
- Callers depend on an interface, not a concrete class
2. Five Patterns
| Pattern | Core question | Idea |
|---|---|---|
| Singleton | How many? | One instance |
| Factory Method | Which class? | Subclass picks the type |
| Abstract Factory | Which family? | Related objects together |
| Builder | How to assemble? | Step-by-step construction |
| Prototype | Copy from what? | Clone; costly to create, cheap to copy |
3. Spot the Need
new ConcreteClass()scattered around- Constructors with many parameters
- Switch on a type tag
Rule of thumb
Start with a plain constructor. Refactor to a pattern only when the variation actually appears.
4. Interview Point
- Why use a creational pattern? Callers depend on an interface, not a class
- Many optional parameters? Builder
- Which question does Abstract Factory answer? Which family?
- When to refactor to a pattern? When the variation appears
Singleton and Factory Method
1. Singleton
- Exactly one instance plus a global access point
- Private constructor, static field, static
getInstance()
2. Thread Safety and Pitfalls
| Init | Thread-safe? |
|---|---|
| Lazy, no lock | No: two threads can both create |
| Lazy + lock | Yes |
Double-checked + volatile | Yes |
| Eager static initializer | Yes |
| Enum singleton | Yes |
- Hidden global state
- Hard to unit test: cannot swap in a mock
- Breaks with one per tenant or per process
Prefer
Dependency injection with a single shared instance, not getInstance() calls everywhere.
3. Factory Method
- Creatorcalls create()
- Subclassoverrides create()
- Productconcrete type
- Base class declares
create(); subclasses choose the concrete product - Use when the class is unknown until runtime or a framework lets users plug in products
- A subclass per product explodes the class count; use a static factory if no extension is needed
Abstract Factory and Builder
1. Abstract Factory
- One interface, one create method per product:
createButton(),createCheckbox() - Each concrete factory returns a matching set: DarkThemeFactory, LightThemeFactory
- Use when products belong together and are never mixed across families
- Typical cases: UI themes, cross-platform widgets, per-database DAOs
| Factory | createButton() | createCheckbox() |
|---|---|---|
| DarkThemeFactory | DarkButton | DarkCheckbox |
| LightThemeFactory | LightButton | LightCheckbox |
Pitfall
Easy to add families, costly to add products: a new product type changes the interface and every concrete factory.
2. Builder
- Separate construction of a complex object from its representation
- Chained calls end in
build(), which returns an immutable result - Cures the telescoping constructor: many optional parameters
java
Pizza p = new Pizza.Builder()
.size(L)
.cheese()
.build(); // validates, then freezeschain the options, build() last
build()must validate required fields, or you get half-built objects- Overkill for objects with two or three fields: use a plain constructor
Prototype and Choosing Between Patterns
1. Prototype
- Prototype: new object by
clone()of an existing instance - Skips costly init: DB loads, heavy config
- Shallow copy shares nested refs; mutating the clone hits the original
- Deep-copy mutable fields; beware circular refs
python
import copy a = {"t": [1]} s, d = a.copy(), copy.deepcopy(a) a["t"].append(2) print(s["t"], d["t"])
shallow copy s sees the change, deep copy d does not
output
[1, 2] [1]
2. Telling Them Apart
| Factory Method | Abstract Factory | |
|---|---|---|
| Makes | one product | a family |
| Via | inheritance | composition (factory object) |
- Factory: finished object in one call
- Builder: assembled in steps, parts vary
- Abstract Factory can use Builders or Prototypes inside
3. Pick Quickly
| Need | Pattern |
|---|---|
| Exactly one | Singleton |
| Many optional params | Builder |
| Class unknown until runtime | Factory Method |
| Matched families | Abstract Factory |
| Costly to create, cheap to copy | Prototype |
Common Mistakes with Creational Patterns
1. Singleton Misuse
- Used as a convenient global: hides dependencies, tests become order-dependent
- Fix: inject the one shared instance
- Lazy init without a lock: two threads can both create it
java
private static volatile Db inst;
static Db get() {
if (inst == null) synchronized (Db.class) {
if (inst == null) inst = new Db();
}
return inst;
}double-checked locking
2. Over-Engineering
- Factory or Builder on a simple object: extra indirection, no variation to justify it
3. Copy and Build Bugs
- Prototype pays off when an object is costly to create but cheap to copy
| Mistake | Symptom | Fix |
|---|---|---|
| Shallow clone with mutable lists | Editing the clone changes the original | Deep copy nested fields |
build() skips validation | Half-built object fails far from the cause | Validate required fields in build() |
4. Remember
Rule of thumb
Add a pattern only when variation appears. Inject singletons, deep-copy clones, validate in build().
Creational Patterns Cheat Sheet
1. Which Pattern
| Pattern | Asks | Pick when |
|---|---|---|
| Singleton | How many? | Need exactly one |
| Factory Method | Which class? | Class unknown until runtime |
| Abstract Factory | Which family? | Matched families |
| Builder | How to assemble? | Many optional params |
| Prototype | Copy from what? | Costly to create; clone instead |
2. Must-Know Facts
- Singleton: private constructor + static
getInstance() - Thread-safe: enum or eager init; inject, don't go global
- Factory Method: subclass overrides
create(), returns one product - Abstract Factory: one interface, a product family; easy to add families, hard to add products
- Builder: stepwise, final
build()validates; often immutable - Prototype:
clone()an object; pick shallow or deep first
3. Pitfalls
- Lazy singleton without a lock: two instances
- Builder without validation in
build(): half-built objects
Shallow vs deep
Mutating a shallow clone changes the original; deep-copy mutable fields.
Check yourself
A lazy Singleton does if (inst == null) inst = new Config();. Two threads call getInstance() first, at the same time. What can go wrong?
- Both see
null, so two instances get created. - Fix: eager init, an enum singleton, or a lock with a
volatilefield.
You clone an Order whose items list is shared (shallow copy), then add an item to the clone. What does the original hold?
- The new item too: both objects point to one list.
- Deep-copy mutable nested fields.
You add createSlider() to an Abstract Factory that already has DarkThemeFactory and LightThemeFactory. What breaks?
- Every concrete factory must implement it.
- Adding products is costly; adding families is easy.