Creational · DWG NO. 002

Factory Method

Push the decision of which concrete class to build into a method that subclasses can override.

TypeCreational
ScopeClass hierarchy
ComplexityLow–Medium
Common inPlugins, drivers, notification channels

The calling code shouldn't need to know every subclass

When a function has to choose between `new EmailNotifier()`, `new SmsNotifier()`, `new PushNotifier()` based on a condition, that condition — and the list of every concrete class — gets duplicated everywhere the object is created. Add a fourth channel and you're hunting down every `if/else` chain in the codebase.

Creation lives behind one method

A creator class declares a factory method. Concrete creators override it to return a specific product. Client code calls the factory method and works only against the product's shared interface — it never names the concrete class directly.

Notifier (abstract) + create(): Channel EmailNotifier SmsNotifier Channel (interface) + send(msg) produces

Notifier factory hierarchy

Client code only ever calls `create()`. Which class gets instantiated depends on which creator you picked once, far from the call site that actually sends the message.

notifiers.js
class Notifier {
  create() {
    throw new Error("Subclasses must implement create()");
  }
  notify(message) {
    const channel = this.create();
    channel.send(message);
  }
}

class EmailNotifier extends Notifier {
  create() {
    return { send: (msg) => console.log(`[email] ${msg}`) };
  }
}

class SmsNotifier extends Notifier {
  create() {
    return { send: (msg) => console.log(`[sms] ${msg}`) };
  }
}

function notifierFor(channel) {
  const registry = { email: EmailNotifier, sms: SmsNotifier };
  const NotifierClass = registry[channel];
  if (!NotifierClass) throw new Error(`Unknown channel: ${channel}`);
  return new NotifierClass();
}

notifierFor("email").notify("Build passed");
notifierFor("sms").notify("Build passed");

Trade-offs