Interview-Ready

Creational Design Patterns

Creational design patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code.

Overview

graph TD
    A[Creational Patterns] --> B[Factory Method]
    A --> C[Abstract Factory]
    A --> D[Builder]
    A --> E[Prototype]
    A --> F[Singleton]
    
    B --> G[Creates objects through inheritance]
    C --> H[Creates families of related objects]
    D --> I[Constructs complex objects step by step]
    E --> J[Creates objects by cloning]
    F --> K[Ensures a single instance]

Patterns

Factory Method

Abstract Factory

Builder

Prototype

Singleton

Comparison

Pattern Creation Method Flexibility Complexity Common Use Cases
Factory Method Inheritance High Low Framework extensions
Abstract Factory Composition Very High Medium Platform independence
Builder Step-by-step High Medium Complex object creation
Prototype Cloning Medium Low Object copying
Singleton Static Low Low Shared resources

Implementation Guidelines

Factory Method

// Creator
public abstract class DocumentCreator {
    public abstract Document createDocument();
    
    public void processDocument() {
        Document doc = createDocument();
        doc.process();
    }
}

// Concrete Creator
public class PDFDocumentCreator extends DocumentCreator {
    @Override
    public Document createDocument() {
        return new PDFDocument();
    }
}

Abstract Factory

// Abstract Factory
public interface GUIFactory {
    Button createButton();
    Checkbox createCheckbox();
}

// Concrete Factory
public class WindowsFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new WindowsButton();
    }
    
    @Override
    public Checkbox createCheckbox() {
        return new WindowsCheckbox();
    }
}

Builder

// Builder
public class ComputerBuilder {
    private Computer computer = new Computer();
    
    public ComputerBuilder addProcessor(String processor) {
        computer.setProcessor(processor);
        return this;
    }
    
    public ComputerBuilder addMemory(int memory) {
        computer.setMemory(memory);
        return this;
    }
    
    public Computer build() {
        return computer;
    }
}

// Usage
Computer computer = new ComputerBuilder()
    .addProcessor("Intel i7")
    .addMemory(16)
    .build();

Prototype

// Prototype
public abstract class Shape implements Cloneable {
    private String id;
    protected String type;
    
    @Override
    public Object clone() {
        Object clone = null;
        try {
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }
}

// Concrete Prototype
public class Rectangle extends Shape {
    public Rectangle() {
        type = "Rectangle";
    }
}

Singleton

// Thread-safe Singleton
public class Singleton {
    private static volatile Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Best Practices

Do’s

  1. Use Factory Method when:
    • You don’t know the exact types of objects you need
    • You want to delegate object creation to subclasses
    • You want to provide hooks for subclasses
  2. Use Abstract Factory when:
    • You need to ensure compatibility between created objects
    • You want to create families of related objects
    • You need to enforce certain combinations of objects
  3. Use Builder when:
    • You need to create complex objects step by step
    • You want to prevent “telescoping constructor” problem
    • You need different representations of the same construction process
  4. Use Prototype when:
    • You need to create objects based on existing instances
    • You want to avoid subclassing in object creation
    • You need to create objects with varying configurations
  5. Use Singleton when:
    • You need exactly one instance of a class
    • You need strict control over global state
    • You need to coordinate actions across the system

Don’ts

  1. Don’t use Singleton as a global state container
  2. Don’t create complex hierarchies with Factory Method
  3. Don’t overuse Abstract Factory for simple object creation
  4. Don’t make Builder patterns overly complex
  5. Don’t use Prototype when object copying is expensive

Anti-Patterns to Avoid

  1. God Object Factory
    • Creating a single factory for all object types
    • Solution: Use separate factories for related object families
  2. Complex Builder Chains
    • Creating long chains of builder methods
    • Solution: Break into smaller, focused builders
  3. Mutable Singletons
    • Creating singletons with mutable state
    • Solution: Make singleton state immutable
  4. Deep Prototype Chains
    • Creating deep hierarchies of prototype objects
    • Solution: Keep prototype hierarchies shallow

Additional Resources