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.

Watch it evolve1 / 8
The problem

A tour creates its narrator

Tour.start() creates a Narrator and has it describe each exhibit on the route.

80
Start
Stage 2
Final

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.

The first versionJava
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());
    }
}
*«create»«record»Exhibit− name: String− fact: String− kidsLine: String− forKids: booleanNarrator~ describe(Exhibit): StringTour− exhibits: List<Exhibit>~ start(): List<String>

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.

A simple factory, and a second switchJava
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 Tour.stops()

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.

Java
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;
    }
}
*«create»«create»«record»Exhibit− name: String− fact: String− kidsLine: String− forKids: boolean«interface»Narrator+ describe(Exhibit): StringAdultNarrator+ describe(Exhibit): StringKidsNarrator+ describe(Exhibit): String«abstract»Tour− exhibits: List<Exhibit>~ createNarrator(): Narrator~ stops(): List<Exhibit>~ start(): List<String>AdultTour~ createNarrator(): NarratorKidsTour~ createNarrator(): Narrator~ stops(): List<Exhibit>
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.

*«create»«create»«record»Exhibit− name: String− fact: String− kidsLine: String− forKids: boolean«interface»Narrator+ describe(Exhibit): StringAdultNarrator+ describe(Exhibit): StringKidsNarrator+ describe(Exhibit): String«abstract»Tour− exhibits: List<Exhibit>~ createNarrator(): Narrator~ stops(): List<Exhibit>~ start(): List<String>AdultTour~ createNarrator(): NarratorKidsTour~ createNarrator(): Narrator~ stops(): List<Exhibit>

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

Narrator

The interface the creator works with

Concrete product

AdultNarrator, KidsNarrator

What actually gets made

Creator

Tour

Declares createNarrator() and uses its result

Concrete creator

AdultTour, KidsTour

Overrides createNarrator() to make its product

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

Integer.valueOf(), List.of()

C++

Factory functions such as std::make_unique() and std::make_shared()

JavaScript

Array.from(), Promise.resolve()

Python

Class methods such as dict.fromkeys() and datetime.fromisoformat()

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 Tour works 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() returns Narrator, 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 std::unique_ptr to the product

JavaScript

A method the base class calls and subclasses define; often replaced by passing a class or a function

Python

An @abstractmethod, or a class attribute naming the product class that subclasses override

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

1. The museum adds an expert tour with longer talks and a route through the archive. What changes in the Factory Method version?
2. Only the narrator varies between tours; routes are all the same. What's the simplest good design?
3. A C++ Tour constructor calls createNarrator(). What happens?
4. Which is a static factory method rather than the Factory Method pattern?

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.