Low-level designCore8 min
Factory Method
When a class creates an object it then uses, let subclasses decide which class that object is. The creator calls its own creation method, and each subclass overrides it to make the product it needs.
A tour creates its narrator
Tour.start() creates a Narrator and has it describe each exhibit on the route.
1 · The situation
An audio tour that creates its narrator
A science museum's app plays audio tours. A Tour walks a list of exhibits, and a Narrator describes each one. Tour.start() creates the narrator it needs and returns the lines to play. There's one kind of tour so far, for adults.
Python writes kids_line, for_kids and, later, create_narrator(); the other names are the same in all four languages.
import java.util.ArrayList;
import java.util.List;
record Exhibit(String name, String fact, String kidsLine, boolean forKids) {}
class Narrator {
String describe(Exhibit e) {
return e.name() + ": " + e.fact();
}
}
class Tour {
private final List<Exhibit> exhibits;
Tour(List<Exhibit> exhibits) {
this.exhibits = List.copyOf(exhibits);
}
List<String> start() {
Narrator narrator = new Narrator();
List<String> lines = new ArrayList<>();
for (Exhibit e : exhibits) lines.add(narrator.describe(e));
return lines;
}
}
public class Main {
static void play(String title, List<String> lines) {
System.out.println(title + ", " + lines.size() + " stops");
for (String line : lines) System.out.println(" " + line);
}
public static void main(String[] args) {
List<Exhibit> hall = List.of(
new Exhibit("Mammoth", "skeleton from the last ice age",
"Its tusks are longer than your bed.", true),
new Exhibit("Sundial", "bronze, cast in 1720", "", false),
new Exhibit("Comet", "replica of a meteorite fall",
"A rock that fell from space.", true));
play("Adult tour", new Tour(hall).start());
}
}The class diagram of this version, drawn from its code: the tour uses one concrete narrator.
Adult tour, 3 stops Mammoth: skeleton from the last ice age Sundial: bronze, cast in 1720 Comet: replica of a meteorite fall
2 · The change
A kids' tour, chosen by a switch
The museum adds a tour for children. It needs a different narrator, one that speaks to kids, and a shorter route that skips exhibits marked as not for kids. The natural first move is an Audience value on the tour and a simple factory: one function that takes the audience and returns the right narrator.
import java.util.ArrayList;
import java.util.List;
record Exhibit(String name, String fact, String kidsLine, boolean forKids) {}
enum Audience { ADULTS, KIDS }
interface Narrator {
String describe(Exhibit e);
}
class AdultNarrator implements Narrator {
public String describe(Exhibit e) {
return e.name() + ": " + e.fact();
}
}
class KidsNarrator implements Narrator {
public String describe(Exhibit e) {
return e.name() + "! " + e.kidsLine();
}
}
final class Narrators {
private Narrators() {}
static Narrator forAudience(Audience audience) {
switch (audience) {
case KIDS: return new KidsNarrator();
default: return new AdultNarrator();
}
}
}
class Tour {
private final List<Exhibit> exhibits;
private final Audience audience;
Tour(List<Exhibit> exhibits, Audience audience) {
this.exhibits = List.copyOf(exhibits);
this.audience = audience;
}
List<Exhibit> stops() {
switch (audience) {
case KIDS: return exhibits.stream().filter(Exhibit::forKids).toList();
default: return exhibits;
}
}
List<String> start() {
Narrator narrator = Narrators.forAudience(audience);
List<String> lines = new ArrayList<>();
for (Exhibit e : stops()) lines.add(narrator.describe(e));
return lines;
}
}
public class Main {
static void play(String title, List<String> lines) {
System.out.println(title + ", " + lines.size() + " stops");
for (String line : lines) System.out.println(" " + line);
}
public static void main(String[] args) {
List<Exhibit> hall = List.of(
new Exhibit("Mammoth", "skeleton from the last ice age",
"Its tusks are longer than your bed.", true),
new Exhibit("Sundial", "bronze, cast in 1720", "", false),
new Exhibit("Comet", "replica of a meteorite fall",
"A rock that fell from space.", true));
play("Adult tour", new Tour(hall, Audience.ADULTS).start());
play("Kids tour", new Tour(hall, Audience.KIDS).start());
}
}Adult tour, 3 stops Mammoth: skeleton from the last ice age Sundial: bronze, cast in 1720 Comet: replica of a meteorite fall Kids tour, 2 stops Mammoth! Its tusks are longer than your bed. Comet! A rock that fell from space.
For choosing a narrator alone, the simple factory is a fine answer: one place decides, and Tour doesn't name a narrator class. But the audience changes more than the narrator. stops() switches on it too, and the next kind of tour, say an expert tour with longer talks and a route through the archive, adds a case to both switches. The checks flag it: Audience is switched on in two places.
What the audience decides | Where it's decided |
|---|---|
Which narrator speaks | The factory function's switch |
Which exhibits are on the route | The switch in |
Anything the next tour adds | Another switch |
3 · The fix
Let each kind of tour create its own narrator
When the creator varies as a whole, not just the object it creates, make each kind of creator a subclass and give it a method to override for the creation:
Tour becomes abstract, with createNarrator() for subclasses to fill in. start() calls it, then walks stops() as before. The tour still uses a narrator; it no longer decides which.
abstract class Tour {
private final List<Exhibit> exhibits;
Tour(List<Exhibit> exhibits) {
this.exhibits = List.copyOf(exhibits);
}
abstract Narrator createNarrator();
List<Exhibit> stops() {
return exhibits;
}
List<String> start() {
Narrator narrator = createNarrator();
List<String> lines = new ArrayList<>();
for (Exhibit e : stops()) lines.add(narrator.describe(e));
return lines;
}
}Adult tour, 3 stops Mammoth: skeleton from the last ice age Sundial: bronze, cast in 1720 Comet: replica of a meteorite fall Kids tour, 2 stops Mammoth! Its tusks are longer than your bed. Comet! A rock that fell from space.
The same tours as the switch version printed.
An expert tour is now one subclass that creates an ExpertNarrator and returns its own route. Nothing that exists changes.
4 · The pattern
Factory Method: subclasses choose the product
Factory Method defines a method for creating an object, and lets subclasses decide which class to instantiate. The creator's other code works only with the product's interface, so a new kind of product arrives with a new subclass of the creator.
The roles in the museum's code: Tour is the creator, each tour subclass a concrete creator, Narrator the product.
Role | In the museum app | Its job |
|---|---|---|
Product |
| The interface the creator works with |
Concrete product |
| What actually gets made |
Creator |
| Declares |
Concrete creator |
| Overrides |
start() asks its own createNarrator() for a narrator, then uses it through the interface.
The checks recognize both Factory Method and Template Method here. That's typical: start() is a fixed algorithm with steps a subclass fills in, and creating the narrator is one of those steps. See Template Method for the other half.
5 · Variants
Simple factories and static factory methods
Two simpler relatives share the name, and both are often the better choice:
- A simple factory is one function or class that creates one of several products from a parameter, usually with a switch. It's what the second version had. Use it when only the product varies.
- A static factory method is a named method that replaces a constructor. It can have a descriptive name, return a cached instance, or return a subtype. Java's
Integer.valueOf()always returns a cached object for values from -128 to 127.
Form | Who decides the class | Use it when |
|---|---|---|
Factory Method | A subclass of the creator, by overriding | The creator varies as a whole, and creation is one of its varying steps |
Simple factory | One function, from a parameter | Only the product varies, and one switch is enough |
Static factory method | The class itself, behind a named method | You want a name, caching or a hidden subtype instead of a bare constructor |
Language | A static factory you've used |
|---|---|
Java |
|
C++ | Factory functions such as |
JavaScript |
|
Python | Class methods such as |
6 · What it costs
A subclass for every product
- Parallel hierarchies. Every new narrator needs a new tour subclass, even if nothing else about the tour differs.
- Inheritance ties. The concrete creators depend on how
Tourworks inside; see Inheritance. - A creation choice fixed by type. A kids' tour always gets a kids' narrator; mixing them at run time takes more subclasses.
When only the product varies, passing it in is simpler: give Tour a narrator, or a function that makes one, in its constructor. That's Dependency injection, and it avoids a subclass per product. In Python and JavaScript, where classes are values, passing the class itself is common. Reach for Factory Method when the subclasses differ in several hooks and creation is one of them, as the route and the narrator do here.
7 · Getting it right
Return the interface, and never call it from a constructor
- Return the product's interface.
createNarrator()returnsNarrator, so the creator can't come to depend on one concrete narrator. - Don't call it from the creator's constructor. In C++, a virtual call during construction runs the base class's version, not the subclass's. In Java, the override runs, but before the subclass's own fields are set.
- Keep creation in one place. If code elsewhere also writes
new KidsNarrator(), the factory method isn't the only place that decides. - Give it a default when one makes sense. A non-abstract
createNarrator()that returns the usual narrator lets most subclasses skip it.
Language | The usual shape |
|---|---|
Java | An abstract method on an abstract class, overridden in each subclass |
C++ | A pure virtual method returning |
JavaScript | A method the base class calls and subclasses define; often replaced by passing a class or a function |
Python | An |
8 · Look-alikes
The creational family
Several patterns hide which class gets created. They differ in what they create and who decides.
Pattern | What it creates | Who decides |
|---|---|---|
Factory Method | One product, used by its creator | A subclass of the creator |
Simple factory | One product, from a parameter | A function with a switch |
Abstract Factory | A family of related products that must match | The factory object the client is given |
Builder | One complex product, step by step | The client, through the builder's steps |
Prototype | A copy of an existing object | Whichever object is copied |
Factory Method and Abstract Factory are most often confused. A factory method is one method on a class that also does other work; an abstract factory is an object whose whole job is to create several related products, and it's often built from factory methods.
9 · In practice
Where you've met it, and what the checks look for
Every Java collection is a creator: iterator() is declared once and each collection overrides it to create its own kind of iterator, which the rest of the library uses through the Iterator interface.
The practice checks recognize a Factory Method when two or more subclasses override a creator method and each creates a different product, and a simple factory when one method creates two or more product types. They warn when the same products are also created with new elsewhere, since then the factory isn't the one place that decides. Design the parking lot and see what they make of your creation code.
Check yourself
4 questions
Takeaways
Remember this
- Factory Method: a creator declares a method to create its product, and subclasses override it to pick the concrete class.
- Use it when the creator varies as a whole and creating the product is one of its varying steps.
- When only the product varies, a simple factory or an injected product is simpler.
- A static factory method is a named constructor: it can cache, hide subtypes and explain itself.
- Never call a factory method from the creator's constructor.