Factory Method
Factory Method moves the decision of which concrete class to create out of the consumer and into a dedicated method.
Introduction
Factory Method moves the decision of which concrete class to create out of the consumer and into a dedicated method. The consumer asks for a PaymentGateway; a subclass or registry returns StripeGateway, AdyenGateway, or a test double. Every framework extension point — Spring @Bean, JUnit test runners, Angular component factories — is Factory Method waiting for an override.
The story
A logistics company added its third carrier integration and found three switch statements scattered across the codebase. A Factory Method on the CarrierGateway hierarchy collapsed them into one creation point — a fourth carrier became one new class plus a registry line.
The business problem
Scattered construction logic violates Open/Closed and slows variant growth:
- Switch sprawl: same if/else chain copied to controllers, jobs, and tests.
- OCP violations: new variant means editing every construction site.
- Duplicated wiring: auth, config, validation repeated at each call site.
- Untestable consumers: hard-coded new Concrete() — no seam for fakes.
The problem teams faced
Before Factory Method, these construction smells appear:
- Multiple switch chains decide which subclass to instantiate.
- Adding a variant requires editing stable modules — regression risk.
- Construction setup duplicated — drift between call sites.
- Tests monkey-patch constructors instead of injecting products.
Understanding the topic
Intent: Define an interface for creating an object; let subclasses or registries decide which class to instantiate.
- Product — interface the factory returns (PaymentGateway).
- ConcreteProduct — StripeGateway, AdyenGateway.
- Creator — declares factoryMethod(); may include template behaviour using the product.
- Registry form (TS) — map key → factory fn; self-register at import time.
Internal architecture
Registry-based Factory Method (TypeScript):
interface PaymentGateway { charge(amount: Money): Promise<Receipt> }const registry = new Map<string, () => PaymentGateway>()export function registerGateway(key: string, factory: () => PaymentGateway) {registry.set(key, factory)}export function createGateway(key: string): PaymentGateway {const factory = registry.get(key)if (!factory) throw new Error(`Unknown gateway: ${key}`)return factory()}
Visual explanation
Three diagrams cover Factory Method flow, forms, and selection:
Informative example
Before / after — switch sprawl to Factory Method:
// ❌ Switch copied everywhere — OCP violationfunction charge(tenant: Tenant, amount: Money) {let gateway: PaymentGatewayswitch (tenant.provider) {case "stripe": gateway = new StripeGateway(tenant.keys); breakcase "adyen": gateway = new AdyenGateway(tenant.keys); breakdefault: throw new Error("unknown")}return gateway.charge(amount)}// ✅ One creation point — new provider = new class + register lineclass PaymentGatewayFactory {constructor(private registry: Map<string, () => PaymentGateway>) {}create(provider: string): PaymentGateway {const factory = this.registry.get(provider)if (!factory) throw new UnknownProviderError(provider)return factory()}}// Bootstrapfactory.register("stripe", () => new StripeGateway(config.stripe))factory.register("adyen", () => new AdyenGateway(config.adyen))
Execution workflow
Client invokes operation
Calls Creator method — not a concrete constructor.
Real-world use
Calendar.getInstance(), URLConnection.openConnection(), Spring BeanFactory, Angular ComponentFactoryResolver, React createElement, every test framework's SUT factory. Frameworks live on Factory Method because they construct types the author never saw.
Production case study
Multi-tenant storage backends:
- Scenario: 6 storage backends; every file-write feature had a 6-way switch.
- Decision: StorageGateway interface + per-tenant Factory Method at session start.
- Outcome: 7th backend removed in one delete; 8th (Wasabi) shipped in 90 lines.
- Metric: construction call sites dropped from 47 to 1.
Trade-offs
- Pro: new variants without editing stable code — OCP.
- Pro: centralized construction and config wiring.
- Con: indirection overkill if variant set is fixed forever.
- Con: subclass form needs parallel Creator + Product hierarchies.
Decision framework
- Use when git log shows new variants arriving regularly.
- Use when construction requires non-trivial setup you won't duplicate.
- Avoid when one implementation forever — a plain constructor is honest.
- Avoid when construction is one line — a function suffices.
Best practices
- Registry + self-register for plugin-style growth in TypeScript.
- Return interfaces — never concrete types to consumers.
- Spring @Bean methods are Factory Methods — treat them as architecture.
Anti-patterns to avoid
- God Factory with 40-way switch — split by domain or use registry.
- Factory that returns concrete types — couples callers to implementations.
- Factory Method "for consistency" on types that never vary.
Common mistakes
- Registry name collisions when two modules register the same key.
- Forgetting to register in test bootstrap — obscure "unknown key" failures.
- Subclass hierarchies when registry would be lighter.
Debugging tips
- Log factory key + concrete class at creation — trace wrong provider bugs.
- Diff registry contents between prod and test containers.
Optimization strategies
- Cache factory products when construction is expensive and product is immutable.
- Lazy registry init — register on first import of each plugin module.
Common misconceptions
- Factory Method ≠ Abstract Factory. Factory Method creates one product; Abstract Factory creates a family.
- Factory Method ≠ Simple Factory. Simple factory is a function; Factory Method is the OCP pattern with override point.
- DI containers are Factory Method registries at scale.
Advanced interview questions
Interview Prep
Practice concise answers, then expand each card for the explanation.
1BeginnerQuestionFactory Method vs new Foo()?+
Answer
Follow-up
2IntermediateQuestionFactory Method vs Abstract Factory?+
Answer
Follow-up
3AdvancedQuestionRegistry vs subclass Factory Method in TypeScript?+
Answer
Follow-up
4AdvancedQuestionIntroduce Factory Method into 12-way switch legacy?+
Answer
Follow-up
Summary
You can collapse switch sprawl into Factory Method, pick subclass vs registry form, and explain OCP payoff. Tell the carrier integration story — if you can do that, you own Factory Method.