The Factory Method Pattern and Turning Levels Into Numbers
The four pieces every factory method setup shares, a common shortcut that looks like this pattern but is not, and where it actually pays off at scale.
The last post built RandomAnimalFactory and BalancedAnimalFactory, both swappable behind the same AnimalFactory shape.
Here's the idea in one sentence: a single hardcoded creation function is a real improvement over copy-pasted logic, but it only becomes this pattern once there's a shared shape more than one factory can implement.
The General Shape
This pattern always has the same four pieces, whatever it's building.
A Single Factory Function Isn't Quite This Pattern
It's tempting to stop one step earlier: just write one function somewhere that randomly builds a dog, cat, or duck, and call that function everywhere you need an animal. That's a real improvement over copy-pasting the random logic everywhere, but it's missing the actual point of this pattern.
A single hardcoded function can't be swapped. There's no AnimalFactory shape for RandomAnimalFactory and BalancedAnimalFactory to both share, so there's nothing to pass around, and nothing calling code could accept instead. The moment you want a second way of deciding what to build, and a way to choose between them at runtime, you need the shared shape, not just one function.
Turning Levels Into Numbers
Here's where this pattern earns its keep at scale. Picture a game that spawns asteroids, and the asteroids need to be bigger and faster on harder levels.
The tempting first move is a class per level: Level1AsteroidFactory, Level2AsteroidFactory, and so on. That's the same class explosion the strategy pattern post ran into with duplicated flying code, just wearing a level number instead.
The fix is the same idea too: instead of a class per level, one AsteroidFactory that takes a size-likelihood and a speed as constructor parameters. Level ten just passes bigger numbers than level one. The level number turns into a property fed into one factory, instead of a whole new class.
g++ -std=c++17 main.cpp -o main && ./main (C++) or npm start (TypeScript) to see it run.Try It Yourself
An e-commerce order needs an alert sent the moment it ships: email for a normal order, SMS for an order over a certain value, and a push notification if the customer has the app installed. Which one gets built depends on the order itself, decided the moment the order ships.
- Design an
Alertproduct with asend()method, and anAlertFactorycreator withcreateAlert(). - Build
EmailAlert,SmsAlert, andPushAlertas concrete products. - Build
OrderAlertFactoryas the concrete creator. ItscreateAlert()should look at the order's value and the customer's app status to decide which one to build. - Call it from a
notifyCustomer(factory, order)function that never mentionsEmailAlert,SmsAlert, orPushAlertby name.
The Essentials
- A single hardcoded factory function is a real improvement over duplicated logic, but it isn't this pattern until there's a shared shape that more than one factory can implement.
- At scale, a factory that takes parameters (like a size-likelihood and a speed) replaces a whole family of near-identical classes, one per level or scenario.
Keep reading