Skip to content
Low Level Design Mastery Logo
LowLevelDesign Mastery

Factory Pattern

Create objects without knowing their exact type - let the factory decide!

Now let’s dive into the Factory Pattern - one of the most commonly used creational design patterns.

Imagine you’re ordering a pizza. You don’t need to know how the kitchen makes it - you just tell them “I want a pizza” and they handle the rest. The Factory Pattern works the same way!

The Factory Pattern lets you create objects without specifying their exact class. Instead of directly creating objects with new ClassName(), you ask a factory to create them for you.

The Factory Pattern is useful when:

  1. You don’t know the exact type of object you need until runtime
  2. Object creation is complex - Lots of setup or configuration needed
  3. You want to decouple object creation from object usage
  4. You need flexibility - Easy to add new types without changing existing code
  5. You want centralized control - All object creation happens in one place

What Happens If We Don’t Use Factory Pattern?

Section titled “What Happens If We Don’t Use Factory Pattern?”

Without the Factory Pattern, you might:

  • Scatter object creation throughout your code
  • Tightly couple your code to specific classes
  • Make it hard to add new types - Need to modify code everywhere
  • Duplicate creation logic - Same setup code in multiple places
  • Violate Open/Closed Principle - Need to modify code to extend functionality

Let’s start with a super simple example that anyone can understand!

Diagram

Here’s how the Factory Pattern works in practice - showing the sequence of interactions:

sequenceDiagram
    participant Client
    participant Factory as PizzaFactory
    participant Interface as Pizza Interface
    participant Concrete as ConcretePizza
    
    Client->>Factory: create_pizza("margherita")
    activate Factory
    Factory->>Concrete: new MargheritaPizza()
    activate Concrete
    Concrete-->>Factory: Pizza instance
    deactivate Concrete
    Factory-->>Client: Returns Pizza instance
    deactivate Factory
    Client->>Interface: pizza.prepare()
    activate Interface
    Interface-->>Client: "Preparing Margherita pizza..."
    deactivate Interface
    
    Note over Client,Concrete: Client doesn't know<br/>about concrete classes!

You’re building a pizza ordering system. Customers can order different types of pizzas (Margherita, Pepperoni, Veggie). Without a factory, you’d do this:

Problems:

  • Need to modify order_pizza() every time you add a new pizza type
  • Creation logic is scattered
  • Hard to test - can’t easily swap implementations
  • Violates Open/Closed Principle
classDiagram
    class Pizza {
        <<abstract>>
        +prepare() str
    }
    class MargheritaPizza {
        +prepare() str
    }
    class PepperoniPizza {
        +prepare() str
    }
    class VeggiePizza {
        +prepare() str
    }
    class PizzaFactory {
        +create_pizza(type) Pizza
    }
    class Client {
        +order_pizza(type) str
    }
    
    Pizza <|-- MargheritaPizza : implements
    Pizza <|-- PepperoniPizza : implements
    Pizza <|-- VeggiePizza : implements
    PizzaFactory ..> Pizza : creates
    Client --> PizzaFactory : uses
    Client ..> Pizza : uses

Real-World Software Example: Payment Processing System

Section titled “Real-World Software Example: Payment Processing System”

Now let’s see a realistic software example - a payment processing system that needs to handle different payment methods.

You’re building an e-commerce system that needs to process payments through different gateways (Stripe, PayPal, Square). Without Factory Pattern:

Problems:

  • Complex creation logic scattered throughout code
  • Need to know different initialization requirements for each gateway
  • Hard to add new payment gateways
  • Configuration management is messy
  • Tight coupling to specific payment classes

With Factory Pattern, adding a new gateway is super easy:


There are several variations of the Factory Pattern:

The simplest form - a single method that creates objects:

Each product has its own factory:

Creates families of related objects:


Use Factory Pattern when:

Object creation is complex - Lots of setup, configuration, or validation
You don’t know the exact type until runtime
You want to decouple creation from usage
You need flexibility - Easy to add new types
You want centralized control - All creation in one place
You’re following Open/Closed Principle - Open for extension, closed for modification

Don’t use Factory Pattern when:

Simple object creation - If new Class() is enough, don’t overcomplicate
Only one type - If you only ever create one type, factory is unnecessary
Creation logic is trivial - Don’t add abstraction for simple cases
Performance is critical - Factory adds a small overhead (usually negligible)


Mistake 3: Factory with Too Many Responsibilities

Section titled “Mistake 3: Factory with Too Many Responsibilities”

  1. Decoupling - Client code doesn’t depend on concrete classes
  2. Flexibility - Easy to add new types without changing existing code
  3. Centralized Control - All creation logic in one place
  4. Hides Complexity - Client doesn’t need to know creation details
  5. Testability - Easy to mock factories for testing
  6. Follows SOLID Principles - Especially Open/Closed and Dependency Inversion

Factory Pattern is a creational design pattern that provides an interface for creating objects without specifying their exact classes. The factory decides which class to instantiate based on the input.

  • Decouple object creation from usage
  • Centralize creation logic
  • Simplify adding new types
  • Hide complex initialization
  • Follow Open/Closed Principle
  1. Define an interface - What all products can do
  2. Create concrete classes - Specific implementations
  3. Create a factory - Handles object creation
  4. Use the factory - Client asks factory to create objects
Client → Factory → Interface → Concrete Classes
  • Client - Uses the factory to get objects
  • Factory - Creates objects based on input
  • Interface - Defines what products can do
  • Concrete Classes - Specific implementations
# Interface
class Pizza(ABC):
@abstractmethod
def prepare(self): pass
# Concrete classes
class MargheritaPizza(Pizza): ...
class PepperoniPizza(Pizza): ...
# Factory
class PizzaFactory:
@staticmethod
def create(type: str) -> Pizza:
if type == "margherita":
return MargheritaPizza()
# ...
# Usage
pizza = PizzaFactory.create("margherita")

✅ Complex object creation
✅ Runtime type determination
✅ Need flexibility to add new types
✅ Want to decouple creation from usage
✅ Following Open/Closed Principle

❌ Simple object creation
❌ Only one type ever created
❌ Trivial creation logic
❌ Performance is critical

  • Factory Pattern = Create objects without knowing exact class
  • Factory = Centralized object creation
  • Interface = Common contract for all products
  • Benefit = Easy to extend, hard to break
  • Principle = Open for extension, closed for modification
# 1. Interface
class Product(ABC):
@abstractmethod
def do_something(self): pass
# 2. Concrete Products
class ProductA(Product): ...
class ProductB(Product): ...
# 3. Factory
class Factory:
@staticmethod
def create(type: str) -> Product:
# Creation logic here
pass
# 4. Usage
product = Factory.create("type_a")
product.do_something()
  • Factory Pattern simplifies object creation
  • It decouples client from concrete classes
  • It makes code more flexible and easier to extend
  • Use it when creation is complex or you need flexibility
  • Don’t use it for simple cases - avoid over-engineering!

What to say:

“Factory Pattern is a creational design pattern that provides an interface for creating objects without specifying their exact classes. The factory decides which class to instantiate based on the input.”

Why it matters:

  • Shows you understand the fundamental purpose
  • Demonstrates knowledge of creational patterns category
  • Indicates you can explain concepts clearly

Must mention:

  • Complex object creation - When initialization involves multiple steps
  • Runtime type determination - When you don’t know the type until runtime
  • Decoupling - When you want to separate creation from usage
  • Extensibility - When you need to easily add new types
  • Open/Closed Principle - When you want to extend without modifying

Example scenario to give:

“I’d use Factory Pattern when building a payment processing system where I need to support multiple payment gateways (Stripe, PayPal, Square). Each gateway has different initialization requirements, and I want to add new gateways without modifying existing code.”

Must explain:

  1. Product Interface - Common interface for all products
  2. Concrete Products - Specific implementations
  3. Factory - Creates products based on input
  4. Client - Uses factory to get products

Visual explanation:

Client → Factory.create(type) → Returns Product → Concrete Implementation

Benefits to mention:

  • Decoupling - Client doesn’t depend on concrete classes
  • Flexibility - Easy to add new types
  • Centralized Control - All creation logic in one place
  • Testability - Easy to mock factories for testing
  • Follows SOLID - Especially Open/Closed Principle

Trade-offs to acknowledge:

  • Complexity - Adds abstraction layer (may be overkill for simple cases)
  • Performance - Small overhead (usually negligible)
  • Over-engineering risk - Don’t use for trivial cases

Q: “What’s the difference between Factory Pattern and Factory Method Pattern?”

A:

“Factory Pattern (Simple Factory) uses a single method to create objects based on input. Factory Method Pattern is more advanced - each product has its own factory class. Factory Method provides more flexibility and follows the Open/Closed Principle better, but Simple Factory is often sufficient for most cases.”

Q: “When would you NOT use Factory Pattern?”

A:

“I wouldn’t use Factory Pattern when object creation is trivial - like creating a simple object with new Class(). Also, if I only ever create one type of object, a factory is unnecessary. I’d avoid it if performance is critical and the overhead matters, though usually the overhead is negligible.”

Q: “How does Factory Pattern relate to SOLID principles?”

A:

“Factory Pattern primarily supports the Open/Closed Principle - you can add new product types without modifying existing code. It also supports Dependency Inversion Principle by making clients depend on abstractions (the interface) rather than concrete classes. Additionally, it can help with Single Responsibility Principle by separating creation logic from business logic.”

Key implementation points:

  1. Use Abstract Base Class (ABC) for the product interface

    from abc import ABC, abstractmethod
    class Product(ABC):
    @abstractmethod
    def do_something(self): pass
  2. Factory method should return the interface type

    def create(type: str) -> Product: # Return interface, not concrete class
  3. Handle error cases - Invalid types should raise exceptions

    if not product_class:
    raise ValueError(f"Unknown type: {type}")
  4. Consider making factory static - If no state needed

    @staticmethod
    def create(type: str) -> Product:

Good examples to mention:

  • Payment Processing - Different payment gateways
  • Database Connections - Different database types (MySQL, PostgreSQL)
  • UI Components - Different button types (Windows, Mac, Linux)
  • Logging Systems - Different log handlers (File, Console, Database)
  • Notification Systems - Different channels (Email, SMS, Push)

Mistakes interviewers watch for:

  1. Over-engineering - Using Factory for simple cases

    • ❌ Bad: Factory for creating simple objects
    • ✅ Good: Factory for complex, multi-step creation
  2. Returning inconsistent types - Factory should return interface type

    • ❌ Bad: Returning different types (string, int, object)
    • ✅ Good: Always return the interface type
  3. Factory doing too much - Factory should only create, not validate/save/log

    • ❌ Bad: Factory creates, validates, saves, and logs
    • ✅ Good: Factory only creates and returns
  4. Not handling errors - Should validate input and raise exceptions

    • ❌ Bad: Returning None for invalid types
    • ✅ Good: Raising ValueError with clear message

Factory vs Builder:

  • Factory - Creates objects of different types (Margherita vs Pepperoni pizza)
  • Builder - Creates objects with complex construction (Pizza with many toppings)

Factory vs Singleton:

  • Factory - Creates multiple instances of different types
  • Singleton - Ensures only one instance exists

Factory vs Abstract Factory:

  • Factory - Creates one type of product
  • Abstract Factory - Creates families of related products

What interviewers look for:

Clean code - Readable, well-structured
Type hints - Proper type annotations
Error handling - Validates input, raises exceptions
Documentation - Clear docstrings
SOLID principles - Follows design principles
Testability - Easy to test and mock

Example of good code:

Before your interview, make sure you can:

  • Define Factory Pattern clearly in one sentence
  • Explain when to use it (with examples)
  • Describe the structure and components
  • List benefits and trade-offs
  • Compare with other creational patterns
  • Implement Factory Pattern from scratch
  • Connect to SOLID principles
  • Identify when NOT to use it
  • Give 2-3 real-world examples
  • Discuss common mistakes and how to avoid them

Interview Questions

Q1. What is the difference between Factory Method and Abstract Factory?
Factory Method creates one type of product (e.g., a specific Pizza), while Abstract Factory creates a family of related products (e.g., a UI theme with Buttons, Windows, and Scrollbars that all match). Think of Factory Method as a single machine, and Abstract Factory as an entire assembly line.
Q2. Does the Factory Pattern violate the Open/Closed Principle?
No, it actually supports it! When you add a new product type, you only need to create a new concrete class and update the factory (or add a new concrete factory in Factory Method). You do not need to modify the client code that uses the factory, so the client code remains closed for modification but open for extension.
Q3. When should you avoid using the Factory Pattern?
Avoid it for simple object creation where direct instantiation (new Class()) is sufficient. If the class structure is not expected to change or grow, adding a factory just introduces unnecessary complexity (over-engineering).

Remember: Factory Pattern is about delegating object creation to a specialized factory, making your code more flexible and maintainable! 🏭