โ† Back to Design Patterns
๐Ÿญ
Creational

Factory Method

Defines an interface for creating objects but lets subclasses or a factory function decide which class to instantiate.

The Problem

Client code needs objects but shouldn't be coupled to their concrete classes. Hard-coding `new ConcreteType()` everywhere makes switching implementations painful.

How It Works

  • โ€บDecouples client code from concrete class implementations
  • โ€บOpen/Closed: add new product types without modifying existing code
  • โ€บThe factory encapsulates all knowledge of which class to create
  • โ€บCan return different subclasses based on input parameters
  • โ€บAbstract Factory extends this by creating families of related objects

When to Use

โœ“When you don't know ahead of time which class to instantiate
โœ“When you want to centralize and control object creation logic
โœ“When subclasses should decide what objects to create
โœ“When creating objects of different types based on runtime config or environment

Real-World Examples

๐ŸŽ
iOS

UITableView.dequeueReusableCell(withIdentifier:), UICollectionView cell registration

๐Ÿค–
Android

ViewModelProvider.Factory, RecyclerView.ViewHolder creation in onCreateViewHolder

๐ŸŒ
Web

React.createElement, document.createElement, Knex query builder

Code Examples

Swift
protocol Notification {
    func send(to recipient: String)
}

struct EmailNotification: Notification {
    func send(to recipient: String) { print("Email โ†’ \(recipient)") }
}

struct PushNotification: Notification {
    func send(to recipient: String) { print("Push โ†’ \(recipient)") }
}

struct SMSNotification: Notification {
    func send(to recipient: String) { print("SMS โ†’ \(recipient)") }
}

// Factory function
enum Channel { case email, push, sms }

func makeNotification(for channel: Channel) -> Notification {
    switch channel {
    case .email: return EmailNotification()
    case .push:  return PushNotification()
    case .sms:   return SMSNotification()
    }
}

let n = makeNotification(for: .push)
n.send(to: "agus@example.com")  // "Push โ†’ agus@example.com"