Skip to content
FollowtheDamnCode

Factory Method

Let subclasses decide which class to create, so the code that uses the product never names a concrete type.

Big idea

Factory Method puts object creation behind an overridable method. The base class writes the workflow — "create an enemy, place it, start its AI" — and calls create_enemy() for the one step that varies. Subclasses override that one method to decide which enemy, and the workflow never mentions a concrete class.

The problem it solves

A game's Level.spawn_wave() was written for the forest level:

enemy = Wolf()
enemy.place(x, y)
enemy.start_ai()

Now there's a desert level with scorpions and an ice level with yetis. Copying spawn_wave three times duplicates the placement and AI logic; adding if self.theme == "desert" puts every theme's knowledge into one method that changes every time a level is added.

Think of it like

A franchise bakery chain. Head office writes the opening routine for every shop: unlock, switch on ovens, bake the signature loaf, open the doors. Each branch decides what its signature loaf is — sourdough in one city, rye in another. Head office never changes its routine when a new branch opens; it just calls "bake the signature loaf".

Structure

The creator's workflow calls a method subclasses override
  • Product (Enemy) — the interface every created object shares.
  • Concrete products (Wolf, Scorpion).
  • Creator (Level) — holds the workflow and declares the factory method.
  • Concrete creators (ForestLevel, DesertLevel) — override the factory method.

In code

One wave routine, a different enemy per level
class Wolf:
    def attack(self): return "bites"

class Scorpion:
    def attack(self): return "stings"

class Yeti:
    def attack(self): return "throws ice"


class Level:
    name = "level"

    def create_enemy(self):                 # the factory method
        raise NotImplementedError

    def spawn_wave(self, n):                # shared workflow, never names a class
        enemies = [self.create_enemy() for _ in range(n)]
        kinds = type(enemies[0]).__name__
        return f"{self.name}: {n} x {kinds}, each {enemies[0].attack()}"


class ForestLevel(Level):
    name = "forest"
    def create_enemy(self): return Wolf()

class DesertLevel(Level):
    name = "desert"
    def create_enemy(self): return Scorpion()

class IceLevel(Level):                       # new level: one small class, no edits elsewhere
    name = "ice"
    def create_enemy(self): return Yeti()


for level in (ForestLevel(), DesertLevel(), IceLevel()):
    print(level.spawn_wave(3))
output
forest: 3 x Wolf, each bites
desert: 3 x Scorpion, each stings
ice: 3 x Yeti, each throws ice

Run and edit it in the app.

Watch it

How to implement it

  1. Give all products a common interface.
  2. In the creator, replace every ConcreteThing() call in the workflow with self.create_thing().
  3. Declare create_thing() in the creator — abstract, or with a sensible default.
  4. Add one concrete creator per variant that overrides it.

In Python, a creator can also take the product class (or any callable) as a constructor argument — Level(enemy_factory=Wolf) — the same idea without subclassing. That's often simpler; the subclass form shines when the creator has other things to override too.

In Factory Method, what do subclasses override?

When to use it — and when not to

Use it when a class can't know ahead of time which objects it must create, when you want subclasses or plug-ins to supply their own products, or when a framework must create objects of user-defined types.

Skip it when there's one product type — Wolf() directly is fine — or when a plain dictionary from names to classes does the job without a class hierarchy.

Pros and cons

ProsCons
Workflow code never names concrete classesA creator subclass per product can bloat the hierarchy
New products without editing the workflow (Open/Closed)Indirection: "where does this object come from?"
Creation logic in one well-named placeOverkill for a single product

How it relates

  • It's Template Method applied to creation: the creation step is the template's hook.
  • Abstract Factory groups several factory methods to create a matching family.
  • Prototype avoids the creator subclass by cloning a sample instead.
  • The refactoring Replace Constructor with Factory Method (Simplifying Method Calls) is the first step towards it.

A function make_enemy(kind) returns Wolf() or Scorpion() based on a string. Is that the Factory Method pattern?

Key terms

  • Factory method — An overridable method that creates and returns a product object.
  • Creator — The class whose workflow calls the factory method.
  • Product — The interface shared by every object the factory method can return.
  • Simple factory — A plain function or class that picks a product, without subclass overriding.