Factory Method
The delivery code does if kind == "road": Truck() elif kind == "sea": Ship().
Move creation into one overridable method, createTransport().
The code that uses an object should not know its exact class
The picture from life
At a pizza shop you say "one veg pizza". You do not pick the dough, the sauce or the oven; the kitchen decides how it is made. If the shop opens a new branch with a different oven, you still order the same way and the pizza arrives.
The problem
What goes wrong when you do not use it.
The delivery code does if kind == "road": Truck() elif kind == "sea": Ship(). Adding air transport means editing that code and every other place that repeats the same ladder.
- creates
- The code that uses an object should not know its exact class
- Subclasses should choose what to create
- You expect new kinds to appear
The idea
How the pattern fixes it.
Move creation into one overridable method, createTransport(). The shared planning code calls it and uses whatever comes back. Each subclass of the creator returns its own product.
- extends (is a)
- creates
Who is who
- Transport
- The product interface: deliver().
- Truck, Ship
- Concrete products.
- Logistics
- The creator. Has planDelivery() that calls the factory method.
- RoadLogistics, SeaLogistics
- Override createTransport() to return Truck or Ship.
Build it in steps
- 1Define the product interface.
- 2Add an abstract createTransport() to the creator.
- 3Let the shared method call it.
- 4Subclass the creator for every kind.
Watch it run
Step through the calls. Tap an arrow to jump to it.
The client asks for a delivery. It never names Ship.
plan_delivery()The code
Python and Java. Try writing the classes yourself from the diagram first.
class Truck:
def deliver(self):
return "truck delivers"
class Ship:
def deliver(self):
return "ship delivers"
class Logistics:
def create_transport(self): # the factory method
raise NotImplementedError
def plan_delivery(self):
return self.create_transport().deliver()
class RoadLogistics(Logistics):
def create_transport(self):
return Truck()
class SeaLogistics(Logistics):
def create_transport(self):
return Ship()
def demo():
return [RoadLogistics().plan_delivery(), SeaLogistics().plan_delivery()]What you gain
- The creator never depends on concrete products
- Adding a product means adding a class, not editing a ladder
What it costs
- A new subclass per product can feel heavy
- For two or three fixed kinds a simple function or map is enough
You have already met it in
- Calendar.getInstance() and NumberFormat.getInstance() in Java
- Frameworks that create your view or controller for you
Often mixed up with
- Abstract FactoryFactory Method makes one product through inheritance; Abstract Factory makes a whole matching family of products.
- BuilderFactory Method picks which class; Builder assembles one complex object step by step.
Make it stick
Write a NotificationSender with a createChannel() factory method and subclasses for Email and SMS. Add Push without editing the shared send() code.