On this page
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:
- The simple factory: one function that picks the class.
- The GoF Factory Method: subclasses choose what to create.
- Where you'll see factories in real libraries.
- 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 for | Because |
|---|---|---|
| Exactly one shared instance, by design | Singleton | Two copies would be a bug (e.g. two connection pools fighting over the same database connections). |
| One place that decides which concrete class to build | Factory 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 together | Abstract Factory | Swapping the factory swaps the whole family, so mismatched pieces can't be combined. |
| An object with many optional parts, assembled step by step | Builder | Named, optional steps instead of long positional constructors; validate once in build(). |
| Many near-identical objects that are expensive to set up | Prototype | Copy a ready-made template instead of repeating costly construction. |
Common mistakes
| Mistake | What happens | Do this instead |
|---|---|---|
The same if/switch on a type string at many call sites | every new type means finding them all | one factory |
A factory switch that grows with every release | the shared module is edited and redeployed for each type | a registry: types register a creator |
| Callers depending on the concrete class the factory returns | the factory can't change what it builds | declare the interface as the return type |
| A subclass per product only to override one creation method | parallel class hierarchies | pass a creator function (TextDocument::new, a class) |
| Building classes from names in user input | arbitrary classes can be instantiated | an 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
switchinside 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@classmethodsuch asdatetime.fromisoformat()), a simple factory (one method picking a class from a parameter), and the GoF Factory Method (an overridable method whose subclasses pick).