← Back to principles

Design Principle

Factory Pattern

Learn the Factory pattern: create objects without specifying exact classes. Master Factory Method and Abstract Factory patterns with practical examples.

Factory Pattern

Why This Matters

Think of the Factory pattern like a restaurant kitchen. You don't tell the kitchen "make me a pizza with these exact ingredients and cooking method." You just say "I want a pizza," and the kitchen (factory) decides how to make it. The Factory pattern does the same for code—you don't specify the exact class to create, you just ask for an object, and the factory creates it.

This matters because object creation can be complex. You might need to choose between different implementations based on configuration, environment, or runtime conditions. The Factory pattern encapsulates this complexity, making your code simpler and more flexible. When you need to add a new type, you just add it to the factory, not everywhere you create objects.

In interviews, when someone asks "How would you refactor this code?", they're testing whether you understand the Factory pattern. Can you identify when object creation is complex? Can you encapsulate it in a factory? Most engineers can't. They create objects directly and wonder why the code is hard to change.

What Engineers Usually Get Wrong

Engineers often think "Factory pattern means always use factories." But factories add indirection. If object creation is simple and unlikely to change, direct instantiation is fine. Use factories when creation is complex, when you need to choose between implementations, or when creation logic might change.

Engineers also confuse Factory Method with Abstract Factory. Factory Method creates one type of object (e.g., create a payment processor). Abstract Factory creates families of related objects (e.g., create a payment processor AND a payment validator). Understanding the difference helps you choose the right pattern.

How This Breaks Systems in the Real World

A service was creating payment processors directly: new CreditCardProcessor(), new PayPalProcessor(), etc. The creation logic was scattered across the codebase. When the team needed to add logging or metrics to all processors, they had to modify code in 20 places. The fix? Use a Factory pattern. Centralize creation logic in one place. Now adding logging or metrics requires changes in only one place.

Another story: A service was using if/else statements to choose which payment processor to create. As more payment methods were added, the if/else chain grew to 50 lines. It became hard to understand and maintain. The fix? Use a Factory pattern with a registry. Register payment processors in a map, and the factory looks them up. Adding a new payment method is now just registering it, not modifying a long if/else chain.


What is the Factory Pattern?

Factory Pattern provides:

  • Encapsulation: Encapsulates object creation logic
  • Decoupling: Decouples object creation from usage
  • Flexibility: Easy to add new types
  • Centralization: Centralizes object creation logic

Types:

  • Factory Method: Subclasses decide which class to instantiate
  • Abstract Factory: Provides interface for creating families of related objects

Factory Method Pattern

Factory Method lets subclasses decide which class to instantiate.

Structure

Creator (abstract)
  ├─ factoryMethod() (abstract)
  └─ createProduct() (uses factoryMethod)

ConcreteCreator
  └─ factoryMethod() (returns ConcreteProduct)

Product (interface)
ConcreteProduct (implements Product)

Examples

Factory Method Example

1// Product interface
2interface Transport {
3 deliver(): void;
4}
5
6// Concrete products
7class Truck implements Transport {
8 deliver(): void {
9 console.log("Delivering by truck");
10 }
11}
12
13class Ship implements Transport {
14 deliver(): void {
15 console.log("Delivering by ship");
16 }
17}

Simple Factory Example

1// Product interface
2interface PaymentMethod {
3 pay(amount: number): void;
4}
5
6// Concrete products
7class CreditCard implements PaymentMethod {
8 pay(amount: number): void {
9 console.log(`Paying ${amount} with credit card`);
10 }
11}
12
13class PayPal implements PaymentMethod {
14 pay(amount: number

Abstract Factory Pattern

1// Abstract products
2interface Button {
3 render(): void;
4}
5
6interface Checkbox {
7 render(): void;
8}
9
10// Concrete products - Windows
11class WindowsButton implements Button {
12 render(): void {
13 console.log("Rendering Windows button");
14 }
15}
16
17class WindowsCheckbox implements Checkbox {
18 render()

Common Pitfalls

  • Over-engineering: Using factory for simple cases. Fix: Use factory only when needed
  • Tight coupling: Factory still coupled to concrete classes. Fix: Use interfaces, dependency injection
  • Complexity: Abstract Factory can be complex. Fix: Use only when creating families of objects
  • Not extensible: Hard to add new types. Fix: Use open-closed principle, registry pattern

Interview Questions

Beginner

Q: What is the Factory pattern and why is it useful?

A:

Factory Pattern provides an interface for creating objects without specifying their exact classes.

Benefits:

  • Encapsulation: Encapsulates object creation logic
  • Decoupling: Decouples object creation from usage
  • Flexibility: Easy to add new types
  • Centralization: Centralizes creation logic

Example:

1class PaymentFactory {
2 static create(type: string): PaymentMethod {
3 switch (type) {
4 case "creditcard":
5 return new CreditCard();
6 case "paypal":
7 return new PayPal();
8 default:
9 throw new Error("Unknown type");
10 }
11 }
12}

Use cases:

  • Object creation complexity: Complex object creation
  • Multiple types: Creating different types of objects
  • Decoupling: Decouple creation from usage
  • Configuration: Creation based on configuration

Intermediate

Q: Explain the difference between Factory Method and Abstract Factory patterns. When would you use each?

A:

Factory Method Pattern:

Lets subclasses decide which class to instantiate:

1abstract class Creator {
2 abstract factoryMethod(): Product;
3
4 create(): Product {
5 return this.factoryMethod();
6 }
7}
8
9class ConcreteCreator extends Creator {
10 factoryMethod(): Product {
11 return new ConcreteProduct();
12 }
13}

Use when:

  • Single product family: Creating one type of product
  • Subclass responsibility: Subclasses decide what to create
  • Flexibility: Need flexibility in product creation

Abstract Factory Pattern:

Provides interface for creating families of related objects:

1interface Factory {
2 createButton(): Button;
3 createCheckbox(): Checkbox;
4}
5
6class WindowsFactory implements Factory {
7 createButton(): Button { return new WindowsButton(); }
8 createCheckbox(): Checkbox { return new WindowsCheckbox(); }
9}

Use when:

  • Multiple product families: Creating families of related objects
  • Consistency: Need consistent products from same family
  • Platform-specific: Different implementations for different platforms

Key Differences:

FeatureFactory MethodAbstract Factory
ProductsSingle productFamily of products
ComplexitySimplerMore complex
Use caseOne product typeMultiple related products

Senior

Q: Design a factory system for a plugin architecture where plugins can be loaded dynamically. How do you handle plugin registration, creation, and ensure type safety?

A:

1// Plugin interface
2interface Plugin {
3 name: string;
4 execute(): void;
5}
6
7// Plugin registry
8class PluginRegistry {
9 private static plugins: Map<string, new () => Plugin> = new Map();
10
11 static register(name: string, pluginClass: new () => Plugin): void {
12 this.plugins.set(name pluginClass

Features:

  1. Plugin registry: Register and store plugins
  2. Dynamic loading: Load plugins at runtime
  3. Type safety: Validate plugin structure
  4. Factory: Create plugins by name

Failure Stories You'll Recognize

The Scattered Creation Logic: A service was creating payment processors directly: new CreditCardProcessor(), new PayPalProcessor(), etc. The creation logic was scattered across the codebase. When the team needed to add logging or metrics to all processors, they had to modify code in 20 places. The fix? Use a Factory pattern. Centralize creation logic in one place. Now adding logging or metrics requires changes in only one place.

The If/Else Chain: A service was using if/else statements to choose which payment processor to create. As more payment methods were added, the if/else chain grew to 50 lines. It became hard to understand and maintain. The fix? Use a Factory pattern with a registry. Register payment processors in a map, and the factory looks them up. Adding a new payment method is now just registering it, not modifying a long if/else chain.

The Over-Engineered Factory: A team created a complex factory system for simple object creation. The factory had 10 layers of abstraction, making it hard to understand and debug. Simple object creation required understanding the entire factory hierarchy. The fix? Simplify. Use factories only when creation is complex or likely to change. For simple creation, direct instantiation is fine.

What Interviewers Are Really Testing

They want to hear you talk about factories as a tool for managing complexity, not a rule to always follow. Junior engineers say "use factories for object creation." Senior engineers say "factories encapsulate creation logic and make code more flexible. Use them when creation is complex or likely to change. Don't over-engineer—simple creation doesn't need factories."

When they ask "How would you refactor this code?", they're testing:

  • Can you identify when object creation is complex?

  • Do you know when to use factories vs direct instantiation?

  • Can you design factories that are simple and maintainable?

  • Factory pattern: Encapsulates object creation logic

  • Factory Method: Subclasses decide which class to instantiate

  • Abstract Factory: Creates families of related objects

  • Benefits: Decoupling, flexibility, centralization

  • Use cases: Complex creation, multiple types, plugin systems

  • Best practices: Use interfaces, make extensible, avoid over-engineering

How InterviewCrafted Will Teach This

We'll teach this through real code problems, not abstract patterns. Instead of memorizing "Factory pattern encapsulates creation," you'll learn through scenarios like "why is it hard to add logging to all payment processors?"

You'll see how factories help you manage complexity and make code more flexible. When an interviewer asks "how would you refactor this code?", you'll think about when factories help and when they don't—not just "use factories."

  • SOLID Principles - Factory pattern follows Open/Closed Principle (open for extension, closed for modification). Understanding SOLID helps understand when to use factories.
  • Builder Pattern - Both patterns handle object creation. Factories choose which class to create, builders construct complex objects step by step.
  • Strategy Pattern - Factories create objects, strategies choose algorithms. Understanding both helps choose the right pattern.
  • DRY, KISS & YAGNI - Factories help eliminate duplication but add complexity. Understanding DRY/KISS/YAGNI helps decide when factories are worth it.
  • Separation of Concerns - Factories separate creation logic from usage. Understanding separation of concerns helps understand factory benefits.

Keep exploring

Principles work best in chorus. Pair this lesson with another concept and observe how your architecture conversations change.