Design Patterns: Creational · Creational Patterns

Factory Method Pattern

Centralizing "which concrete class to instantiate" in one place, so callers depend only on an abstraction.

  • 6 min read
  • 13 practice questions
  • 3 graded problems
On this page
  1. Description
  2. The pattern
  3. The GoF Factory Method: subclasses choose
  4. Where you'll see it
  5. Choosing among the creational patterns
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

Factory Method is the creational pattern for "I need a notification (or document, or connection), and something else should decide which concrete class". It moves the choice of class out of the code that uses the object, into one place - a factory function, or a method that subclasses override. This skill covers the everyday simple factory, the original Gang of Four form, and how to keep a factory open to new types.

Code that needs a notification sender often ends up deciding which class to build by itself:

Java

Notification n;
if (type.equals("EMAIL")) n = new EmailNotification();
else if (type.equals("SMS")) n = new SmsNotification();
else throw new IllegalArgumentException(type);

Python

if kind == "EMAIL":
    n = EmailNotification()
elif kind == "SMS":
    n = SmsNotification()
else:
    raise ValueError(kind)

Copy that into ten call sites and adding push notifications means finding all ten - and every one of them now depends on every concrete class. Construction logic has leaked into code that only wanted a Notification.

We'll go from simple to more technical:

  1. The simple factory: one function that picks the class.
  2. The GoF Factory Method: subclasses choose what to create.
  3. Where you'll see factories in real libraries.
  4. Keeping a factory open to new types, and choosing among the creational patterns.

The pattern

Java

interface Notification { void send(String message); }
class NotificationFactory {
    static Notification create(String type) {
        return switch (type) {
            case "EMAIL" -> new EmailNotification();
            case "SMS" -> new SmsNotification();
            default -> throw new IllegalArgumentException(type);
        };
    }
}
// caller: Notification n = NotificationFactory.create(type); n.send(msg);

Python

from abc import ABC, abstractmethod


class Notification(ABC):
    @abstractmethod
    def send(self, message: str) -> None: ...


def create_notification(kind: str) -> Notification:
    match kind:
        case "EMAIL":
            return EmailNotification()
        case "SMS":
            return SmsNotification()
        case _:
            raise ValueError(kind)

# caller: n = create_notification(kind); n.send(msg)

Callers never write new EmailNotification() themselves - they ask the factory for a Notification and get on with using it.

In Python a simple factory is usually a plain module-level function, and since a class is itself callable, the table-driven version is just a dict: CHANNELS = {"EMAIL": EmailNotification, "SMS": SmsNotification}, then CHANNELS[kind]().

This form - one method that picks a class from a parameter - is usually called a simple factory. It is the everyday version, but it isn't quite the pattern the Gang of Four named Factory Method. That one comes next.

The GoF Factory Method: subclasses choose

In the original pattern the choice is not a parameter but an overridable method. A base class writes its logic once against an interface and leaves one step - creating the product - to its subclasses:

Java

abstract class Editor {                              // the "creator"
    private final List<Document> open = new ArrayList<>();

    protected abstract Document createDocument();     // the factory method

    final void newDocument() {                        // shared logic, written once
        Document d = createDocument();
        open.add(d);
        d.showWelcomeText();
    }
}

class TextEditor extends Editor {
    @Override protected Document createDocument() { return new TextDocument(); }
}
class SheetEditor extends Editor {
    @Override protected Document createDocument() { return new SheetDocument(); }
}

Python

from abc import ABC, abstractmethod


class Editor(ABC):                                  # the "creator"
    def __init__(self):
        self._open: list[Document] = []

    @abstractmethod
    def create_document(self) -> Document: ...      # the factory method

    def new_document(self) -> None:                 # shared logic, written once
        d = self.create_document()
        self._open.append(d)
        d.show_welcome_text()


class TextEditor(Editor):
    def create_document(self) -> Document:
        return TextDocument()


class SheetEditor(Editor):
    def create_document(self) -> Document:
        return SheetDocument()

Editor never names a concrete document class. A framework can ship Editor today and let applications written later decide what it creates - by subclassing, with no parameter and no switch. Use this form when the choice follows from which subclass you are (which application, which platform); use a simple factory when it depends on runtime data (a type string from a request).

Where you'll see it

Logging frameworks (LoggerFactory.getLogger(name)) and database drivers picking a concrete Connection implementation based on a URL scheme (jdbc:postgresql://... vs jdbc:mysql://...) both route construction through one factory call rather than making every caller know every concrete class. Python's logging.getLogger(name) and SQLAlchemy's create_engine("postgresql://..."), which picks a dialect and driver from the URL, do the same.

Choosing among the creational patterns

If you need...Reach forBecause
Exactly one shared instance, by designSingletonTwo copies would be a bug (e.g. two connection pools fighting over the same database connections).
One place that decides which concrete class to buildFactory Method (this lesson)Callers depend on an interface; adding a type changes the factory, not every caller.
A matched family of related objects that must vary togetherAbstract FactorySwapping the factory swaps the whole family, so mismatched pieces can't be combined.
An object with many optional parts, assembled step by stepBuilderNamed, optional steps instead of long positional constructors; validate once in build().
Many near-identical objects that are expensive to set upPrototypeCopy a ready-made template instead of repeating costly construction.

Common mistakes

MistakeWhat happensDo this instead
The same if/switch on a type string at many call sitesevery new type means finding them allone factory
A factory switch that grows with every releasethe shared module is edited and redeployed for each typea registry: types register a creator
Callers depending on the concrete class the factory returnsthe factory can't change what it buildsdeclare the interface as the return type
A subclass per product only to override one creation methodparallel class hierarchiespass a creator function (TextDocument::new, a class)
Building classes from names in user inputarbitrary classes can be instantiatedan allowlisted registry of known types

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. NotificationFactory.create(type), a static method with a switch - simple factory or GoF Factory Method?

Show answer

A simple factory: one method choosing a class from a parameter. The GoF form would be an overridable createNotification() that each subclass implements.

2. Adding PushNotification to the switch-based factory - what has to change, and what would a registry change?

Show answer

The factory's switch must be edited, and its module redeployed, for every new type. With a registry (a Map<String, Supplier<Notification>> filled at startup), a new type registers itself and the factory's code stays the same.

3. Why must every product a factory returns share a supertype?

Show answer

The factory's return type is that supertype, and callers only ever use it. That is what lets them stay ignorant of the concrete class.

Key takeaways

  • Callers ask for an interface (Notification), never for a concrete class.
  • All "which class do I build?" logic lives in one place, so adding a type is a one-place change.
  • A growing switch inside the factory is fine at first; a registry (type -> creator) keeps it open for extension.
  • Three things share the name: static factory methods like List.of() (a named alternative to a constructor - in Python, a @classmethod such as datetime.fromisoformat()), a simple factory (one method picking a class from a parameter), and the GoF Factory Method (an overridable method whose subclasses pick).

Quick revise

The whole skill on one page, for the night before an interview.

Everything from the lesson and the deep dive on one page. Skim it before practice or an interview, and open the full lesson for anything that doesn't ring a bell.

Pick the tool

The choice of class depends on...Use
Runtime data (a type string)a simple factory, ideally backed by a registry
Which subclass you are (framework vs application)the GoF Factory Method (an overridable creation step)
Only the product, nothing elsepass a creator: Supplier<T> / a class or callable
Readable alternative constructorsstatic factories (of, from) / @classmethods
Per-use objects inside a long-lived servicean injected provider

Snippets worth knowing by heart

Java

private static final Map<String, Supplier<Notification>> CHANNELS = Map.of(
    "EMAIL", EmailNotification::new,
    "SMS", SmsNotification::new);

static Notification create(String type) {
    Supplier<Notification> s = CHANNELS.get(type);
    if (s == null) throw new IllegalArgumentException("unknown channel " + type + "; expected " + CHANNELS.keySet());
    return s.get();
}

Python

CHANNELS: dict[str, type[Notification]] = {"EMAIL": EmailNotification, "SMS": SmsNotification}

def create_notification(kind: str) -> Notification:
    try:
        return CHANNELS[kind]()
    except KeyError:
        raise ValueError(f"unknown channel {kind!r}; expected {sorted(CHANNELS)}") from None

The traps

  • The same switch at many call sites.
  • An ever-growing factory switch - use a registry.
  • Callers depending on the concrete class returned.
  • Classes built from names in input - allowlist.

Staff-level points, one line each

  • Static factories have names, can cache (Integer.valueOf: 127 same object, 128 not) and return hidden subtypes.
  • List.of returns List12/ListN; EnumSet returns RegularEnumSet (≤64) or JumboEnumSet; Path("x") is a PosixPath - code to the declared type.
  • Identity comparisons of factory results are unreliable - compare values.
  • GoF Factory Method creates parallel hierarchies; a Supplier/callable often replaces the subclass.
  • Providers give long-lived objects fresh short-lived ones - and give tests a seam.
  • Reflection-based factories fed by input are an arbitrary-instantiation vulnerability.
  • Unknown keys: fail fast with the valid options; a Null Object only when "do nothing" is intended.

Say it in an interview

  • "Callers ask the factory for a Notification; only the factory knows the classes."
  • "New channels register themselves, so the factory never changes."
  • "The editor takes TextDocument::new - no subclass just to pick the document type."
  • "The type comes from user input, so it goes through an allowlist, never Class.forName."

Practice

This skill comes with 13 multiple-choice questions that tell you which misconception a wrong answer reveals and 3 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Factory Method Pattern free

Route Notifications via a Factory Method

MediumProCoding problemJava · Python

Implement NotificationFactory.create(type) so it returns the right Notification implementation for "EMAIL" or "SMS", without the caller (Solution.dispatch) ever needing to know which concrete class it got back.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Add Shapes Without Editing the Factory

MediumProCoding problemJava · Python

A switch inside a factory works - until every new type means editing and recompiling the factory. A registry-based factory fixes that: each type registers a creator function, and the factory just looks it up.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Let Each Editor Create Its Own Documents

MediumProCoding problemJava · Python

The Factory Method pattern in its original (Gang of Four) form: a base class writes its logic once against an interface, and leaves one step - creating the product - to its subclasses.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Factory Method Pattern free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime