Factory means: one place builds your objects, and callers just say what kind they want.
You do not scatter type checks or new calls across the app. You ask a factory for “a payment of this kind,” and it decides which concrete class to build.
To build it you usually:
Hide the decision in one function or class.
Return a shared interface, not the concrete type.
Let new kinds plug in without editing old code.
The problem it solves
Without a factory, the same type check gets copied into every screen that needs a payment object.
interface Payment { charge(amount: number): string;}class CardPayment implements Payment { charge(amount: number) { return `Charged ${amount} via card`; }}class WalletPayment implements Payment { charge(amount: number) { return `Charged ${amount} via wallet`; }}function createPayment(kind: string): Payment { if (kind === 'card') return new CardPayment(); if (kind === 'wallet') return new WalletPayment(); throw new Error(`Unknown kind: ${kind}`);}// useconst payment = createPayment('card');console.log(payment.charge(42.5));
This is not one of the original design patterns — just good hygiene. One place knows how to build a Payment; everyone else just calls it.
Factory Method: let subtypes decide
The simple factory still has one if/switch someone edits for every new kind. Factory Method moves that decision into subtypes instead, using overriding rather than conditionals.
public abstract class PaymentProcessor { public abstract Payment createPayment(); public void processOrder(double amount) { Payment payment = createPayment(); System.out.println(payment.charge(amount)); }}public class CardProcessor extends PaymentProcessor { public Payment createPayment() { return new CardPayment(); }}public class WalletProcessor extends PaymentProcessor { public Payment createPayment() { return new WalletPayment(); }}// usePaymentProcessor processor = new CardProcessor();processor.processOrder(42.5);
Now there is no if/else anywhere. Adding a new kind means adding a new subtype, not editing old code.
Registry-based factory: the practical default
A registry-based factory keeps a lookup from “kind” to “implementation” and just reads from it at runtime. Each implementation reports its own kind, so the factory never needs to know every concrete type up front.
interface PaymentProcessor { kind: string; process(amount: number): void;}class CardProcessor implements PaymentProcessor { kind = 'card'; process(amount: number) { console.log(`Processing card payment: ${amount}`); }}class WalletProcessor implements PaymentProcessor { kind = 'wallet'; process(amount: number) { console.log(`Processing wallet payment: ${amount}`); }}class PaymentProcessorFactory { private processors = new Map<string, PaymentProcessor>(); constructor(all: PaymentProcessor[]) { for (const p of all) this.processors.set(p.kind, p); } get(kind: string) { const p = this.processors.get(kind); if (!p) throw new Error(`No processor for: ${kind}`); return p; }}// useconst factory = new PaymentProcessorFactory([new CardProcessor(), new WalletProcessor()]);factory.get('card').process(42.5);
Adding a crypto processor later means writing one new class and registering it — nothing above changes.
When it helps
Use Factory when the exact type depends on something you only know at runtime — a string from a request, a flag on an order — and there is more than one implementation callers should not need to tell apart.
Ask: “If a new kind shows up next year, how many files change?” With a good factory, the answer is one new file.
When it hurts
Do not wrap a single class in a factory. new CardPayment() is already clear — factory.createPayment() just adds a hop with nothing to hide.
Watch out for a factory that grows its own if/else chain as new types show up. That is a sign to move to a registry.
Practical tips
Start with a simple factory for a small, stable set of types.
Use Factory Method when subclasses each build their own related object.
Use a registry once the set of types keeps growing.
Always return the interface, never the concrete type.
Fail loudly on an unknown kind — never return null silently.
Check your understanding
Factory in one line?
One place builds objects; callers just name the kind they want.
Simple factory vs Factory Method?
Simple factory is one if/switch. Factory Method pushes the choice into subclasses, no conditionals.
Why is a registry the practical default?
New kinds register themselves. The factory code never needs to change.
Main risk of overusing Factory?
Wrapping a single implementation in a factory adds a hop that hides nothing.
What to learn next
Next: Builder Pattern — assemble a complex object step by step, then produce the finished result.