Skip to main content

Creational Patterns

Level: Patterns | Track: Low Level Design

Why it matters in interviews

Creational patterns appear in nearly every machine-coding problem because every system creates objects. Being able to say "I'll use a Factory Method here because the caller shouldn't know the concrete type" demonstrates design fluency. Builder in particular is a must-have — as soon as a constructor exceeds three parameters, interviewers expect you to reach for it.

Core concepts

  • Factory Method — defines a creation interface; subclasses (or a factory class) decide which concrete class to instantiate; the caller only depends on the interface
  • Abstract Factory — creates families of related objects (e.g., a UIFactory that produces Button + Checkbox + TextField in a consistent theme) without specifying concrete classes
  • Builder — constructs a complex object step by step; separates construction logic from the object itself; ideal when an object has many optional parameters
  • Singleton — restricts instantiation to one instance; useful for shared resources (config, connection pool) but makes unit testing hard — prefer dependency injection instead
  • Prototype — creates new objects by cloning an existing one; useful when construction is expensive and new instances are similar to existing ones

Code example

// ===== Factory Method =====
interface Vehicle { void drive(); }

class Car implements Vehicle { public void drive() { System.out.println("Car driving"); } }
class Truck implements Vehicle { public void drive() { System.out.println("Truck driving"); } }
class Motorcycle implements Vehicle { public void drive() { System.out.println("Bike riding"); } }

class VehicleFactory {
public static Vehicle create(String type) {
return switch (type.toUpperCase()) {
case "CAR" -> new Car();
case "TRUCK" -> new Truck();
case "MOTORCYCLE" -> new Motorcycle();
default -> throw new IllegalArgumentException("Unknown vehicle: " + type);
};
}
}

// Caller knows only the interface:
Vehicle v = VehicleFactory.create("CAR");
v.drive(); // "Car driving"


// ===== Builder =====
class Pizza {
enum Size { SMALL, MEDIUM, LARGE }

private final Size size; // required
private final List<String> toppings;
private final boolean extraCheese;
private final String crustType;

private Pizza(Builder b) {
this.size = b.size;
this.toppings = Collections.unmodifiableList(b.toppings);
this.extraCheese = b.extraCheese;
this.crustType = b.crustType;
}

public static class Builder {
private final Size size;
private List<String> toppings = new ArrayList<>();
private boolean extraCheese = false;
private String crustType = "thin";

public Builder(Size size) { this.size = size; }

public Builder addTopping(String t) { toppings.add(t); return this; }
public Builder extraCheese() { extraCheese = true; return this; }
public Builder crustType(String c) { crustType = c; return this; }
public Pizza build() { return new Pizza(this); }
}
}

Pizza pizza = new Pizza.Builder(Pizza.Size.LARGE)
.addTopping("CHEESE")
.addTopping("MUSHROOM")
.extraCheese()
.crustType("thick")
.build();


// ===== Singleton (thread-safe, double-checked locking) =====
class ConfigManager {
private static volatile ConfigManager instance;

private ConfigManager() {} // private constructor

public static ConfigManager getInstance() {
if (instance == null) {
synchronized (ConfigManager.class) {
if (instance == null) instance = new ConfigManager();
}
}
return instance;
}
}

When to use

PatternUse when...
Factory MethodThe caller should not know or care which concrete class is created; type varies at runtime
Abstract FactoryYou need to swap entire families of related objects (e.g., dark theme vs light theme across all widgets)
BuilderConstructor has more than 3 parameters, or parameters are optional/combinatorial
SingletonTruly one shared resource (thread pool, config, registry) — prefer DI frameworks over hand-rolled Singleton
PrototypeObject construction is expensive (e.g., deep DB query) and new objects are minor variations of existing ones

Common interview mistakes

  • Singleton overuse — reaching for Singleton for every shared object; use it only when there must be exactly one instance globally; test code becomes very painful with Singletons
  • Factory when Builder is needed — if you pass 5 parameters to VehicleFactory.create(type, color, seats, fuelType, transmission), stop and use a Vehicle.Builder instead
  • Mutable Builder result — forgetting to copy the list inside the Builder before assigning to the immutable object; the caller can still mutate the original list reference
  • Abstract Factory vs Factory Method confusion — Factory Method creates one product type; Abstract Factory creates a family of related product types in a coordinated way

Covered by Problems

ProblemLink
Vending Machine
Parking Lot System
Library Management System
Hotel Booking System

Resources