Factory Pattern Explained

Master the art of creating objects without tight coupling to concrete classes

The Problem: Tight Coupling

When you use new everywhere, your code becomes rigid:

// Client code tightly coupled to concrete classes
function processPayment(type: string, amount: number) {
  let processor
  if (type === 'credit') {
    processor = new CreditCardProcessor()
  } else if (type === 'paypal') {
    processor = new PayPalProcessor()
  } else if (type === 'crypto') {
    processor = new CryptoProcessor()
  }
  return processor.process(amount)
}

What's wrong? Adding new payment types requires modifying this function.

The Solution: Factory Method

Factory Method defines an interface for creating objects, but lets subclasses decide which class to instantiate.

Key Benefits:

  • Decouples object creation from usage
  • Easy to extend with new types
  • Follows Open/Closed Principle
  • Single place to manage creation logic

Before Factory: Tight Coupling

// Every module that needs a logger creates it directly
class UserService {
  private logger = new ConsoleLogger() // Coupled!

  createUser(data: UserData) {
    this.logger.log('Creating user...')
    // ...
  }
}

class OrderService {
  private logger = new ConsoleLogger() // Duplicated!

  processOrder(order: Order) {
    this.logger.log('Processing order...')
    // ...
  }
}

Problems: Hard to change logger type, difficult to test, repeated instantiation

After Factory: Loose Coupling

// Logger interface
interface ILogger {
  log(message: string): void
}

// Factory creates loggers based on environment
class LoggerFactory {
  static create(): ILogger {
    const env = process.env.NODE_ENV
    if (env === 'production') {
      return new CloudLogger()
    } else if (env === 'development') {
      return new ConsoleLogger()
    }
    return new FileLogger()
  }
}

// Services now depend on abstraction
class UserService {
  private logger = LoggerFactory.create()
  // ...
}

Better: Centralized creation, easy to test, flexible configuration

Real-World Use Cases

1. UI Component Libraries

Creating platform-specific UI elements (iOS vs Android vs Web)

2. Document Generation

Word, PDF, Excel exporters with a common interface

3. Payment Processing

Credit card, PayPal, crypto, bank transfer processors

4. Database Connections

MySQL, PostgreSQL, MongoDB connectors based on config

5. Notification Systems

Email, SMS, push, in-app notifications

Pattern: When you have multiple implementations of a common interface

When to Use / When NOT to Use

Use Factory When:

  • You have multiple implementations of an interface
  • Types might change based on configuration
  • You want to decouple object creation from usage
  • Creation logic is complex or reused in many places

Skip Factory When:

  • You only have one or two types that rarely change
  • Creation is trivial (just new ClassName())
  • It adds unnecessary complexity to simple code
  • The indirection makes code harder to understand

Rule of thumb: Don't use patterns just because they exist. Use them when they solve a real problem.

Factory Method vs Simple Factory

Simple Factory

// Just a function/class that creates objects
class ShapeFactory {
  static create(type: string): Shape {
    if (type === 'circle') return new Circle()
    if (type === 'square') return new Square()
    throw new Error('Unknown type')
  }
}

Not a formal pattern. Just a helper function.

Factory Method

// Uses inheritance - subclasses decide what to create
abstract class ShapeCreator {
  abstract createShape(): Shape

  render() {
    const shape = this.createShape()
    shape.draw()
  }
}

class CircleCreator extends ShapeCreator {
  createShape(): Shape {
    return new Circle()
  }
}

Formal pattern. Uses inheritance and polymorphism.

Key Takeaways

  1. Factory Method separates object creation from object usage
  2. Promotes loose coupling and extensibility
  3. Makes code easier to test (inject mock factories)
  4. Centralizes creation logic in one place
  5. Don't overuse—only when you have multiple types or complex creation

Remember: The goal is cleaner, more maintainable code—not just applying patterns for the sake of it.

Interview Tip

Be ready to explain the difference between Simple Factory, Factory Method, and Abstract Factory. Most real-world code uses Simple Factory because it's simpler!

1 / 0
Factory Pattern Explained