Factory Method Pattern

7 min read

Reading Progress0%
Object-Oriented Design Index
Tier 1 -- Foundations
Tier 2
Tier 3
Tier 4
Tier 5
Object-Oriented Design Index
Tier 1 -- Foundations
Tier 2
Tier 3
Tier 4
Tier 5

Factory Method Pattern

1. What Is It?

The Method pattern defines an for creating an object but lets subclasses decide which concrete class to instantiate. Rather than calling new ConcreteProduct() directly in your business logic, you call an abstract create() method — and each subclass provides the actual instantiation. The creator class can then operate on the product through its without knowing or caring about the specific type it received.

Without this pattern, client code that needs to create objects becomes littered with if/else or switch blocks that must be updated every time a new type is added. This violates the Open/Closed Principle: the existing creation logic must be modified to support extension. Method isolates that variation point in a dedicated override.


QUICK CHECK

A payment processing service currently supports credit card and PayPal payments using a large switch statement in the checkout logic. Every time a new payment method is added, a developer must modify that switch statement. Which problem does this design exhibit, and how does the Factory Method pattern address it?

Choose one answer

2. How It Works

Step-by-step mechanics:

  1. Define a Product with the operations all product variants must support.
  2. Implement ConcreteProduct classes for each variant.
  3. Define a Creator (or ) with an abstract factory_method() that returns Product.
  4. Put the core business logic in Creator — it uses factory_method() to get a product but never knows the concrete type.
  5. Create ConcreteCreator subclasses that each override factory_method() to return a specific ConcreteProduct.

Python

from abc import ABC, abstractmethod
from typing import Protocol


class Button(Protocol):
    """Interface all button types must satisfy."""

    def render(self) -> None: ...
    def on_click(self, handler: callable) -> None: ...


class HtmlButton:
    def render(self) -> None:
        print("<button>Click me</button>")

    def on_click(self, handler: callable) -> None:
        print(f"Bound JS handler: {handler.__name__}")


class WindowsButton:
    def render(self) -> None:
        print("[Win32 Button: Click me]")

    def on_click(self, handler: callable) -> None:
        print(f"Registered Win32 callback: {handler.__name__}")


class Dialog(ABC):
    """Creator — contains the core business logic; delegates creation to subclasses."""

    @abstractmethod
    def create_button(self) -> Button:
        """Factory method — subclasses decide which button type to produce."""
        ...

    def render_window(self) -> None:
        button = self.create_button()   # uses the product via the interface
        button.render()


class HtmlDialog(Dialog):
    def create_button(self) -> Button:
        return HtmlButton()


class WindowsDialog(Dialog):
    def create_button(self) -> Button:
        return WindowsButton()


def build_dialog(os_type: str) -> Dialog:
    """Client code — picks a ConcreteCreator based on configuration."""
    creators: dict[str, type[Dialog]] = {
        "web": HtmlDialog,
        "windows": WindowsDialog,
    }
    creator_class = creators.get(os_type)
    if creator_class is None:
        raise ValueError(f"Unknown OS type: {os_type}")
    return creator_class()


dialog = build_dialog("web")
dialog.render_window()   # <button>Click me</button>

Java

// Product interface
public interface Button {
    void render();
    void onClick(Runnable handler);
}

// Concrete products
public class HtmlButton implements Button {
    @Override public void render() { System.out.println("<button>Click me</button>"); }
    @Override public void onClick(Runnable handler) {
        System.out.println("Bound JS handler");
        handler.run();
    }
}

public class WindowsButton implements Button {
    @Override public void render() { System.out.println("[Win32 Button: Click me]"); }
    @Override public void onClick(Runnable handler) {
        System.out.println("Registered Win32 callback");
        handler.run();
    }
}

// Creator — abstract class with the factory method hook
public abstract class Dialog {
    // Factory method — subclasses override this
    public abstract Button createButton();

    // Core business logic uses the product via the interface
    public void renderWindow() {
        Button button = createButton();
        button.render();
    }
}

// Concrete creators
public class HtmlDialog extends Dialog {
    @Override public Button createButton() { return new HtmlButton(); }
}

public class WindowsDialog extends Dialog {
    @Override public Button createButton() { return new WindowsButton(); }
}

// Client
public class Application {
    public static Dialog configure(String osType) {
        return switch (osType) {
            case "web"     -> new HtmlDialog();
            case "windows" -> new WindowsDialog();
            default -> throw new IllegalArgumentException("Unknown OS: " + osType);
        };
    }

    public static void main(String[] args) {
        Dialog dialog = configure("web");
        dialog.renderWindow();  // <button>Click me</button>
    }
}

QUICK CHECK

In a Factory Method pattern, the abstract Dialog class contains a render_window() method that calls self.create_button() internally. Why does Dialog call its own factory method rather than directly instantiating a specific button class like HtmlButton?

Choose one answer

3. Variants & Comparisons

ApproachHow It WorksProsConsBest For
Factory MethodSubclass overrides one creation hookSimple; single product; follows inheritance hierarchyOne new class per variantWhen subclassing is already occurring
Abstract FactorySeparate factory object creates a family of related productsConsistent product families; no subclassing requiredMore classes; harder to extend with new productsWhen products must be used together (e.g., UI theme)
Static Factory Methodstatic method returns instances (not GoF)Convenient; can return cached/subtype instancesNot overridable; less polymorphicUtility creation (Optional.of, List.of)
Simple Factory (not GoF)One class with a create(type) switchEasy to understandViolates OCP; grows indefinitelyThrowaway prototypes

Designs often start with Method and evolve into Abstract when multiple coordinated product families are needed.


QUICK CHECK

A frontend team is building a design system that supports multiple UI themes (e.g., Light and Dark). Each theme requires a consistent set of coordinated components — buttons, modals, and tooltips — that must all match the same theme. Which factory pattern variant is the best fit, and why?

Choose one answer

4. When to Use It (and When NOT To)

Use Method when:

  • A class cannot anticipate the type of objects it needs to create.
  • Subclasses should control which concrete product is instantiated.
  • You want to isolate product-creation logic so that adding a new product type requires only one new class, not touching existing ones.

Do NOT use Method when:

  • You only have one product type that will never vary — it adds unnecessary indirection.
  • The variation is configuration-driven (a dictionary/map lookup is simpler).
  • You need to create families of related products — use Abstract Factory instead.

Anti-patterns:

  • Factory everywhere — applying Factory Method to every class adds classes without benefit when the creation logic is trivial.
  • Creator doing too much — the Creator class should contain business logic that uses the product; if it's just a creation class, consider using a simple factory function.

Decision triggers:

  • "I have a base class whose subclasses should vary the type of object created" → Factory Method
  • "I need to add new product types without modifying existing code" → Factory Method satisfies OCP here
  • "I need consistent sets of related objects (e.g., all Windows widgets or all Mac widgets)" → Abstract Factory

QUICK CHECK

A backend team is building a notification service that currently sends only email alerts. The team is certain it will need to add SMS, push notifications, and Slack messages over the next few months, each requiring different initialization logic. A junior developer suggests using the Factory Method pattern. A senior developer pushes back, saying the variation is purely configuration-driven and a map lookup would be simpler. Which scenario would most strongly justify the junior developer's position and make Factory Method the better choice?

Choose one answer

5. Real-World Usage

Java java.util.Calendar.getInstance() This is a static method that returns a locale- and timezone-appropriate Calendar subclass. The caller never instantiates Calendar directly — the method selects the right concrete implementation (GregorianCalendar, JapaneseImperialCalendar, etc.) based on the system locale. This is the Factory Method intent applied at a static level.

javax.xml.parsers.DocumentBuilderFactory DocumentBuilderFactory.newInstance() returns a platform-specific factory, and factory.newDocumentBuilder() is the factory method that produces a DocumentBuilder. This decouples client XML-parsing code from any specific parser implementation (Xerces, JDK built-in, etc.).

Python unittest framework unittest.TestLoader.loadTestsFromTestCase() acts as a factory method that creates TestSuite objects. Subclassing TestLoader and overriding loadTests* methods lets frameworks customize how tests are collected — a real-world application of the Factory Method hook.


QUICK CHECK

Your team is building an XML processing service that must run on multiple platforms, each with a different underlying parser implementation. You want client code to remain completely decoupled from any specific parser. Which design approach best matches how javax.xml.parsers.DocumentBuilderFactory solves this same problem?

Choose one answer

6. Interview Cheat Sheet

Key sentences to demonstrate depth:

  1. " Method doesn't just move new to another place — the key insight is that the Creator contains real business logic that operates on the product through an , making the creation variation a hook, not the whole point."
  2. "Every time you add a new product type, you add one new ConcreteCreator subclass. The existing Creator and all other ConcreteCreators are untouched — this is the Open/Closed Principle in action."
  3. "The difference between Method and Abstract Factory is vs. : Factory Method uses subclass overrides to vary the product; Abstract Factory uses a separate factory object to create a consistent family of products."
  4. "Static factory methods like Optional.of() share the name but not the pattern — they can't be overridden, so you can't use to vary the product type."

Common follow-up questions:

  • "When would you use Abstract Factory instead?" → When you need to ensure a family of related objects are used together (e.g., a UI toolkit where buttons, dialogs, and checkboxes must all match the same platform theme).
  • "How is this different from just a constructor?" → A factory method can return a subtype, return a cached instance, have a descriptive name, and be overridden in subclasses — none of which a constructor can do.
  • "What's the Open/Closed connection?" → New product types require new subclasses, not changes to existing code. The base Creator class is closed for modification but open for extension.

Connections to other concepts:

  • Factory Method is often a special case of where the hook method creates an object rather than performing a step.
  • Abstract Factory — a natural evolution when multiple related product types need to be coordinated.
  • Factory Method breaks the direct dependency on ConcreteProduct by having the Creator depend only on the Product .
Glossary History

Click dotted jargon to save explanations here.

Glossary History

Click dotted jargon to save explanations here.