Handbooks / OOP & Design Patterns / Chapter 3

Creational Patterns

7 pages · ~5 min✓ Reviewed

Builds on SOLID Principles. Next up: Structural Patterns.

OOP & Design PatternsPage 1

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

PatternCore questionIdea
SingletonHow many?One instance
Factory MethodWhich class?Subclass picks the type
Abstract FactoryWhich family?Related objects together
BuilderHow to assemble?Step-by-step construction
PrototypeCopy 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
OOP & Design PatternsPage 2

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

InitThread-safe?
Lazy, no lockNo: two threads can both create
Lazy + lockYes
Double-checked + volatileYes
Eager static initializerYes
Enum singletonYes
  • 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

  1. Creatorcalls create()
  2. Subclassoverrides create()
  3. 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
OOP & Design PatternsPage 3

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
FactorycreateButton()createCheckbox()
DarkThemeFactoryDarkButtonDarkCheckbox
LightThemeFactoryLightButtonLightCheckbox
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 freezes

chain 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
OOP & Design PatternsPage 4

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 MethodAbstract Factory
Makesone producta family
Viainheritancecomposition (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

NeedPattern
Exactly oneSingleton
Many optional paramsBuilder
Class unknown until runtimeFactory Method
Matched familiesAbstract Factory
Costly to create, cheap to copyPrototype
OOP & Design PatternsPage 5

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
MistakeSymptomFix
Shallow clone with mutable listsEditing the clone changes the originalDeep copy nested fields
build() skips validationHalf-built object fails far from the causeValidate required fields in build()

4. Remember

Rule of thumb

Add a pattern only when variation appears. Inject singletons, deep-copy clones, validate in build().

OOP & Design PatternsPage 6

Creational Patterns Cheat Sheet

1. Which Pattern

PatternAsksPick when
SingletonHow many?Need exactly one
Factory MethodWhich class?Class unknown until runtime
Abstract FactoryWhich family?Matched families
BuilderHow to assemble?Many optional params
PrototypeCopy 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.

OOP & Design PatternsPage 7

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 volatile field.
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.