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 interface2interface Transport {3 deliver(): void;4}56// Concrete products7class Truck implements Transport {8 deliver(): void {9 console.log("Delivering by truck");10 }11}1213class Ship implements Transport {14 deliver(): void {15 console.log("Delivering by ship");16 }17}
Simple Factory Example
1// Product interface2interface PaymentMethod {3 pay(amount: number): void;4}56// Concrete products7class CreditCard implements PaymentMethod {8 pay(amount: number): void {9 console.log(`Paying ${amount} with credit card`);10 }11}1213class PayPal implements PaymentMethod {14 pay(amount: number
Abstract Factory Pattern
1// Abstract products2interface Button {3 render(): void;4}56interface Checkbox {7 render(): void;8}910// Concrete products - Windows11class WindowsButton implements Button {12 render(): void {13 console.log("Rendering Windows button");14 }15}1617class 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;34 create(): Product {5 return this.factoryMethod();6 }7}89class 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}56class 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:
| Feature | Factory Method | Abstract Factory |
|---|---|---|
| Products | Single product | Family of products |
| Complexity | Simpler | More complex |
| Use case | One product type | Multiple 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 interface2interface Plugin {3 name: string;4 execute(): void;5}67// Plugin registry8class PluginRegistry {9 private static plugins: Map<string, new () => Plugin> = new Map();1011 static register(name: string, pluginClass: new () => Plugin): void {12 this.plugins.set(name pluginClass
Features:
- Plugin registry: Register and store plugins
- Dynamic loading: Load plugins at runtime
- Type safety: Validate plugin structure
- 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.
Key Takeaways
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
Related Topics
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.