Creational patterns deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. They provide flexibility in what gets created, who creates it, how it gets created, and when.

Abstract Factory

Problem: Need to create families of related or dependent objects without specifying their concrete classes.

When to use:

  • System should be independent of how its products are created
  • System should be configured with one of multiple families of products
  • Family of related product objects must be used together
  • You want to reveal only interfaces, not implementations

Structure:

  • Provides an interface for creating families of related objects
  • Concrete factories implement the creation methods
  • Client uses abstract interfaces only

Example (TypeScript):

// Abstract products
interface Button { render(): void; }
interface Checkbox { render(): void; }
 
// Abstract factory
interface GUIFactory {
  createButton(): Button;
  createCheckbox(): Checkbox;
}
 
// Concrete Windows implementations
class WindowsButton implements Button {
  render() { console.log('Rendering Windows button'); }
}
class WindowsCheckbox implements Checkbox {
  render() { console.log('Rendering Windows checkbox'); }
}
 
// Concrete Windows factory
class WindowsFactory implements GUIFactory {
  createButton(): Button { return new WindowsButton(); }
  createCheckbox(): Checkbox { return new WindowsCheckbox(); }
}
 
// Concrete macOS implementations
class MacButton implements Button {
  render() { console.log('Rendering Mac button'); }
}
class MacCheckbox implements Checkbox {
  render() { console.log('Rendering Mac checkbox'); }
}
 
// Concrete macOS factory
class MacFactory implements GUIFactory {
  createButton(): Button { return new MacButton(); }
  createCheckbox(): Checkbox { return new MacCheckbox(); }
}
 
// Client code
class Application {
  constructor(private factory: GUIFactory) {}
 
  createUI() {
    const button = this.factory.createButton();
    const checkbox = this.factory.createCheckbox();
    button.render();
    checkbox.render();
  }
}
 
// Usage
const os = process.platform;
const factory = os === 'darwin' ? new MacFactory() : new WindowsFactory();
const app = new Application(factory);
app.createUI();

Example (Ruby):

# Abstract products
class Button
  def render
    raise NotImplementedError
  end
end
 
class Checkbox
  def render
    raise NotImplementedError
  end
end
 
# Abstract factory
class GUIFactory
  def create_button
    raise NotImplementedError
  end
 
  def create_checkbox
    raise NotImplementedError
  end
end
 
# Concrete Windows implementations
class WindowsButton < Button
  def render
    puts 'Rendering Windows button'
  end
end
 
class WindowsCheckbox < Checkbox
  def render
    puts 'Rendering Windows checkbox'
  end
end
 
# Concrete Windows factory
class WindowsFactory < GUIFactory
  def create_button
    WindowsButton.new
  end
 
  def create_checkbox
    WindowsCheckbox.new
  end
end
 
# Usage
factory = WindowsFactory.new
button = factory.create_button
checkbox = factory.create_checkbox
button.render
checkbox.render

Builder

Problem: Too many constructor parameters make object creation complex and error-prone.

When to use:

  • Construction process must allow different representations
  • Object has many optional parameters
  • Step-by-step construction is needed
  • Construction logic is complex

Structure:

  • Builder interface defines steps to construct product
  • Concrete builders implement these steps
  • Director orchestrates the building process (optional)
  • Product is the complex object being built

Example (TypeScript):

class Pizza {
  size?: string;
  cheese?: boolean;
  pepperoni?: boolean;
  mushrooms?: boolean;
 
  describe() {
    console.log(`${this.size} pizza with ${this.cheese ? 'cheese' : 'no cheese'}`);
  }
}
 
class PizzaBuilder {
  private pizza: Pizza = new Pizza();
 
  setSize(size: string): this {
    this.pizza.size = size;
    return this;
  }
 
  addCheese(): this {
    this.pizza.cheese = true;
    return this;
  }
 
  addPepperoni(): this {
    this.pizza.pepperoni = true;
    return this;
  }
 
  addMushrooms(): this {
    this.pizza.mushrooms = true;
    return this;
  }
 
  build(): Pizza {
    return this.pizza;
  }
}
 
// Usage
const pizza = new PizzaBuilder()
  .setSize('large')
  .addCheese()
  .addPepperoni()
  .build();

Example (Ruby):

class Pizza
  attr_accessor :size, :cheese, :pepperoni, :mushrooms
 
  def describe
    puts "#{size} pizza with #{cheese ? 'cheese' : 'no cheese'}"
  end
end
 
class PizzaBuilder
  def initialize
    @pizza = Pizza.new
  end
 
  def size(value)
    @pizza.size = value
    self
  end
 
  def add_cheese
    @pizza.cheese = true
    self
  end
 
  def add_pepperoni
    @pizza.pepperoni = true
    self
  end
 
  def add_mushrooms
    @pizza.mushrooms = true
    self
  end
 
  def build
    @pizza
  end
end
 
# Usage
pizza = PizzaBuilder.new
  .size('large')
  .add_cheese
  .add_pepperoni
  .build

Factory Method

Problem: Need to defer instantiation to subclasses, letting them decide which class to instantiate.

When to use:

  • Class can’t anticipate the type of objects it needs to create
  • Class wants its subclasses to specify objects to create
  • Classes delegate responsibility to helper subclasses

Structure:

  • Creator declares factory method returning product
  • Concrete creators override factory method
  • Products share common interface

Example (TypeScript):

interface Document {
  open(): void;
  save(): void;
}
 
abstract class Application {
  abstract createDocument(): Document;
 
  newDocument() {
    const doc = this.createDocument();
    doc.open();
    return doc;
  }
}
 
class PDFDocument implements Document {
  open() { console.log('Opening PDF'); }
  save() { console.log('Saving PDF'); }
}
 
class WordDocument implements Document {
  open() { console.log('Opening Word doc'); }
  save() { console.log('Saving Word doc'); }
}
 
class PDFApplication extends Application {
  createDocument(): Document {
    return new PDFDocument();
  }
}
 
class WordApplication extends Application {
  createDocument(): Document {
    return new WordDocument();
  }
}
 
// Usage
const app: Application = new PDFApplication();
const doc = app.newDocument(); // Opens PDF

Example (Ruby):

class Document
  def open
    raise NotImplementedError
  end
 
  def save
    raise NotImplementedError
  end
end
 
class Application
  def create_document
    raise NotImplementedError
  end
 
  def new_document
    doc = create_document
    doc.open
    doc
  end
end
 
class PDFDocument < Document
  def open
    puts 'Opening PDF'
  end
 
  def save
    puts 'Saving PDF'
  end
end
 
class PDFApplication < Application
  def create_document
    PDFDocument.new
  end
end
 
# Usage
app = PDFApplication.new
doc = app.new_document

Prototype

Problem: Need to create new objects by copying existing ones, especially when creation is expensive.

When to use:

  • Objects to create are specified by prototypical instance
  • Classes to instantiate are specified at runtime
  • Avoid building parallel class hierarchies of factories
  • Instances have few state combinations

Structure:

  • Prototype interface declares cloning method
  • Concrete prototypes implement cloning
  • Client creates new objects by cloning prototype

Example (TypeScript):

interface Cloneable {
  clone(): this;
}
 
class Shape implements Cloneable {
  constructor(
    public x: number,
    public y: number,
    public color: string
  ) {}
 
  clone(): this {
    return Object.create(this);
  }
}
 
class Circle extends Shape {
  constructor(
    x: number,
    y: number,
    color: string,
    public radius: number
  ) {
    super(x, y, color);
  }
}
 
// Usage
const redCircle = new Circle(10, 20, 'red', 5);
const blueCircle = redCircle.clone();
blueCircle.color = 'blue';
blueCircle.x = 30;

Example (Ruby):

class Shape
  attr_accessor :x, :y, :color
 
  def initialize(x, y, color)
    @x = x
    @y = y
    @color = color
  end
 
  def clone
    self.dup
  end
end
 
class Circle < Shape
  attr_accessor :radius
 
  def initialize(x, y, color, radius)
    super(x, y, color)
    @radius = radius
  end
end
 
# Usage
red_circle = Circle.new(10, 20, 'red', 5)
blue_circle = red_circle.clone
blue_circle.color = 'blue'
blue_circle.x = 30

Singleton

Problem: Need exactly one instance of a class, with global access point.

When to use:

  • There must be exactly one instance of a class
  • Instance must be accessible from well-known access point
  • Sole instance should be extensible by subclassing

Warning: Singletons are often considered an anti-pattern. They introduce global state and make testing difficult. Consider dependency injection instead.

Example (TypeScript):

class Database {
  private static instance: Database;
  private connection: any;
 
  private constructor() {
    // Private constructor prevents direct instantiation
    this.connection = this.connect();
  }
 
  private connect() {
    console.log('Connecting to database...');
    return { /* connection object */ };
  }
 
  static getInstance(): Database {
    if (!Database.instance) {
      Database.instance = new Database();
    }
    return Database.instance;
  }
 
  query(sql: string) {
    console.log(`Executing: ${sql}`);
  }
}
 
// Usage
const db1 = Database.getInstance();
const db2 = Database.getInstance();
console.log(db1 === db2); // true

Example (Ruby):

require 'singleton'
 
class Database
  include Singleton
 
  def initialize
    @connection = connect
  end
 
  def connect
    puts 'Connecting to database...'
    # connection object
  end
 
  def query(sql)
    puts "Executing: #{sql}"
  end
end
 
# Usage
db1 = Database.instance
db2 = Database.instance
puts db1 == db2 # true

Better alternative (Dependency Injection):

class Database {
  constructor(private config: DbConfig) {
    this.connection = this.connect();
  }
 
  private connect() { /* ... */ }
  query(sql: string) { /* ... */ }
}
 
// Single instance managed by DI container
const db = new Database(config);
 
// Inject where needed
class UserRepository {
  constructor(private db: Database) {}
 
  findById(id: number) {
    return this.db.query(`SELECT * FROM users WHERE id = ${id}`);
  }
}
 
const userRepo = new UserRepository(db);

See Also