Factory Method
Factory Method delegates object creation to a method that subclasses can override, so a base class can work with products whose exact type it doesn't need to know.
Problem
A logistics module starts out delivering only by road, so `Logistics.planDelivery()` builds a `Truck` object right inside its own method body. Months later, sea shipping is added. Every place that constructed a `Truck` directly now needs an `if/else` or `switch` on delivery mode, and that branching logic tends to get copy-pasted into every method that needs a transport rather than living in one place.
The underlying issue is that the class doing the useful work - planning the delivery, tracking it, logging it - is welded to one specific concrete class it happens to instantiate, so extending it to a new mode of transport means editing code that has nothing to do with that new mode conceptually.
Solution
Pull the `new Truck(...)` call out of the method body and into its own method - the factory method - and have the rest of the class call that method instead of a constructor directly. The base class only knows that the factory method returns something implementing a common `Transport` interface; it never names a concrete class.
Each delivery mode then becomes a subclass that overrides just the factory method: `RoadLogistics` returns a `Truck`, `SeaLogistics` returns a `Ship`. All the shared logic - planning, tracking, logging - stays untouched in the base class and automatically works with whatever concrete product the subclass decides to hand back.
When to Use
- Reach for it when a class can't know in advance the exact type of object it will need to create - that decision belongs to a subclass or is resolved at runtime.
- Reach for it when you're building a library or framework and want callers to be able to plug in their own product types without modifying your internal code.
- Reach for it when several classes share almost identical logic and differ only in which kind of object they instantiate at one specific point.
Real-World Examples
- **`document.createElement(tag)`** - the browser returns different concrete element classes (`HTMLButtonElement`, `HTMLInputElement`) behind the same `Element` interface, chosen by the tag argument.
- **Java's `Calendar.getInstance()`** - returns a locale-appropriate concrete calendar subclass (Gregorian, Buddhist, Japanese) without the caller ever choosing the class directly.
- **Logger factories in frameworks like Log4j/SLF4J** - `LoggerFactory.getLogger()` hands back a concrete logger implementation appropriate for the configured backend, behind one common `Logger` interface.
Structure
The Creator hosts the shared workflow and calls its own factory method whenever it needs a new product, without knowing which concrete class that method will return. Each ConcreteCreator overrides just the factory method to supply one specific ConcreteProduct, and every product - regardless of which creator built it - is used through the same Product interface.
Participants
Contains the workflow that needs a product and calls its own factory method to obtain one, optionally with a default implementation.
In the diagram: Creator.
Overrides only the factory method, returning one specific ConcreteProduct instance while inheriting all the shared workflow unchanged.
In the diagram: RoadLogistics.
The interface every product must satisfy, letting the Creator's workflow stay identical regardless of which concrete product is actually in play.
In the diagram: Product.
A specific class implementing Product, instantiated only inside the matching ConcreteCreator's factory method.
In the diagram: Truck.
class Transport {
deliver() {
throw new Error('deliver() must be implemented');
}
}
class Truck extends Transport {
deliver() {
return 'Deliver by land in a box';
}
}
class Ship extends Transport {
deliver() {
return 'Deliver by sea in a container';
}
}
class Logistics {
createTransport() {
throw new Error('createTransport() must be implemented');
}
planDelivery() {
const transport = this.createTransport();
return `Planning: ${transport.deliver()}`;
}
}
class RoadLogistics extends Logistics {
createTransport() {
return new Truck();
}
}
class SeaLogistics extends Logistics {
createTransport() {
return new Ship();
}
}
const road = new RoadLogistics();
console.log(road.planDelivery());
const sea = new SeaLogistics();
console.log(sea.planDelivery());
Step 1 of 6
Transport is the abstract product
`deliver()` throws unless a subclass overrides it - this defines what every concrete transport must be able to do, without saying which vehicle it is.
Step 2 of 6
Truck and Ship are concrete products
Each subclass overrides `deliver()` with its own description - `Truck` delivers by land, `Ship` delivers by sea. Both satisfy the same `Transport` contract.
Step 3 of 6
Logistics declares the factory method and uses its result
`createTransport()` is left unimplemented here, but `planDelivery()` already calls `this.createTransport()` and works with whatever it gets back - the creator class depends only on the abstract `Transport` type.
Step 4 of 6
RoadLogistics overrides the factory method to produce a Truck
`createTransport()` here returns `new Truck()` - this single override is what makes `planDelivery()` (inherited unchanged from `Logistics`) deliver by land.
Step 5 of 6
SeaLogistics overrides the factory method to produce a Ship
`createTransport()` here returns `new Ship()` instead - `planDelivery()` is identical code in both subclasses, but each override sends it down a different delivery path.
Step 6 of 6
planDelivery() behaves differently depending only on the subclass
`road.planDelivery()` and `sea.planDelivery()` run the exact same inherited method body, yet print different results - the only thing that changed is which `createTransport()` override got called.
interface Transport {
deliver(): string;
}
class Truck implements Transport {
deliver(): string {
return 'Deliver by land in a box';
}
}
class Ship implements Transport {
deliver(): string {
return 'Deliver by sea in a container';
}
}
abstract class Logistics {
abstract createTransport(): Transport;
planDelivery(): string {
const transport = this.createTransport();
return `Planning: ${transport.deliver()}`;
}
}
class RoadLogistics extends Logistics {
createTransport(): Transport {
return new Truck();
}
}
class SeaLogistics extends Logistics {
createTransport(): Transport {
return new Ship();
}
}
const road = new RoadLogistics();
console.log(road.planDelivery());
const sea = new SeaLogistics();
console.log(sea.planDelivery());
interface Transport { String deliver(); }
class Truck implements Transport { public String deliver() { return "Deliver by land in a box"; } }
class Ship implements Transport { public String deliver() { return "Deliver by sea in a container"; } }
abstract class Logistics {
public abstract Transport createTransport();
public String planDelivery() {
return "Planning: " + createTransport().deliver();
}
}
class RoadLogistics extends Logistics {
public Transport createTransport() { return new Truck(); }
}
class SeaLogistics extends Logistics {
public Transport createTransport() { return new Ship(); }
}
Logistics road = new RoadLogistics();
System.out.println(road.planDelivery());
Logistics sea = new SeaLogistics();
System.out.println(sea.planDelivery());
interface ITransport { string Deliver(); }
class Truck : ITransport { public string Deliver() => "Deliver by land in a box"; }
class Ship : ITransport { public string Deliver() => "Deliver by sea in a container"; }
abstract class Logistics
{
public abstract ITransport CreateTransport();
public string PlanDelivery() => $"Planning: {CreateTransport().Deliver()}";
}
class RoadLogistics : Logistics { public override ITransport CreateTransport() => new Truck(); }
class SeaLogistics : Logistics { public override ITransport CreateTransport() => new Ship(); }
Logistics road = new RoadLogistics();
Console.WriteLine(road.PlanDelivery());
Logistics sea = new SeaLogistics();
Console.WriteLine(sea.PlanDelivery());
from abc import ABC, abstractmethod
class Transport(ABC):
@abstractmethod
def deliver(self) -> str:
pass
class Truck(Transport):
def deliver(self) -> str:
return 'Deliver by land in a box'
class Ship(Transport):
def deliver(self) -> str:
return 'Deliver by sea in a container'
class Logistics(ABC):
@abstractmethod
def create_transport(self) -> Transport:
pass
def plan_delivery(self) -> str:
transport = self.create_transport()
return f'Planning: {transport.deliver()}'
class RoadLogistics(Logistics):
def create_transport(self) -> Transport:
return Truck()
class SeaLogistics(Logistics):
def create_transport(self) -> Transport:
return Ship()
road = RoadLogistics()
print(road.plan_delivery())
sea = SeaLogistics()
print(sea.plan_delivery())
Step 1 of 6
Transport is the abstract product
`Transport(ABC)` declares `deliver()` as `@abstractmethod`, so it defines a contract every concrete transport must fulfill without dictating which vehicle it is.
Step 2 of 6
Truck and Ship are concrete products
Each subclass implements `deliver()` with its own text - `Truck` delivers by land, `Ship` delivers by sea - both satisfying the same `Transport` contract.
Step 3 of 6
Logistics declares the factory method and uses its result
`create_transport()` is abstract here, but `plan_delivery()` already calls `self.create_transport()` and formats whatever it returns - `Logistics` depends only on the abstract `Transport` type.
Step 4 of 6
RoadLogistics overrides the factory method to produce a Truck
This subclass's `create_transport()` returns `Truck()` - the one override that makes the inherited `plan_delivery()` deliver by land.
Step 5 of 6
SeaLogistics overrides the factory method to produce a Ship
This subclass's `create_transport()` returns `Ship()` instead - `plan_delivery()` is unchanged, only the product it builds on differs.
Step 6 of 6
plan_delivery() behaves differently depending only on the subclass
`road.plan_delivery()` and `sea.plan_delivery()` run the same inherited method, but print different results because each instance's `create_transport()` override supplied a different `Transport`.
Click "Run" to execute this code in a sandboxed frame and see console output here.
TypeScript runs as plain JavaScript here - type annotations are stripped, not type-checked.
Click "Run" to execute this code in a sandboxed frame and see console output here.
Advantages
- The base class's workflow never names a concrete product class, so it stays valid no matter how many product types get added later.
- All the object-creation decisions for a family of related classes live in one overridable method instead of being copy-pasted wherever a new instance is needed.
- Adding a new product variant is purely additive - write one new ConcreteCreator/ConcreteProduct pair, and nothing in the existing hierarchy has to change.
Disadvantages
- Each new product variant typically means a new subclass, so a family with many variants can produce a sizeable parallel class hierarchy.
- For a class that only ever needs to build one type of object, introducing a factory method is pure overhead with no payoff.
Question 1 of 10
What does Factory Method's intent say the pattern delegates to an overridable method?
Correct! The base class only knows the factory method returns something implementing the Transport interface, not which concrete class it is.
Not quite. The base class only knows the factory method returns something implementing the Transport interface, not which concrete class it is.
Question 2 of 10
According to the pattern's drawbacks, what happens as a product family grows to many variants?
Correct! Each new product variant typically means a new subclass, so a family with many variants can produce a sizeable parallel class hierarchy.
Not quite. Each new product variant typically means a new subclass, so a family with many variants can produce a sizeable parallel class hierarchy.
Question 3 of 10
In the class diagram, what does the Creator participant do?
Correct! The Creator contains the workflow that needs a product and calls its own factory method to obtain one, without knowing which concrete class that method returns.
Not quite. The Creator contains the workflow that needs a product and calls its own factory method to obtain one, without knowing which concrete class that method returns.
Question 4 of 10
Which listed real-world example matches Factory Method's shape?
Correct! document.createElement(tag) returns different concrete element classes (HTMLButtonElement, HTMLInputElement) behind the same Element interface, chosen by the tag argument - the same shape as a factory method.
Not quite. document.createElement(tag) returns different concrete element classes (HTMLButtonElement, HTMLInputElement) behind the same Element interface, chosen by the tag argument - the same shape as a factory method.
Question 5 of 10
In the logistics example, what forces a Truck object to be built directly inside planDelivery()?
Correct! The logistics module started out delivering only by road, so planDelivery() built a Truck right inside its own method body - a design that broke down once sea shipping was added.
Not quite. The logistics module started out delivering only by road, so planDelivery() built a Truck right inside its own method body - a design that broke down once sea shipping was added.
Question 6 of 10
In the solution, how does RoadLogistics differ from SeaLogistics?
Correct! Each delivery mode becomes a subclass overriding just the factory method; all shared logic - planning, tracking, logging - stays untouched in the base class.
Not quite. Each delivery mode becomes a subclass overriding just the factory method; all shared logic - planning, tracking, logging - stays untouched in the base class.
Question 7 of 10
What goes wrong once sea shipping is added, if every call site still constructs a Truck directly?
Correct! Every place that constructed a Truck directly now needs an if/else on delivery mode, and that branching logic tends to get copy-pasted into every method that needs a transport.
Not quite. Every place that constructed a Truck directly now needs an if/else on delivery mode, and that branching logic tends to get copy-pasted into every method that needs a transport.
Question 8 of 10
Why does planDelivery() work correctly for both RoadLogistics and SeaLogistics without any change to its own code?
Correct! The Product interface lets the Creator's workflow stay identical regardless of which concrete product is actually in play, since planDelivery() only calls deliver() through that shared interface.
Not quite. The Product interface lets the Creator's workflow stay identical regardless of which concrete product is actually in play, since planDelivery() only calls deliver() through that shared interface.
Question 9 of 10
What is a stated benefit of adding a new product variant with Factory Method?
Correct! Adding a new product variant is purely additive - write one new ConcreteCreator/ConcreteProduct pair, and nothing in the existing hierarchy has to change.
Not quite. Adding a new product variant is purely additive - write one new ConcreteCreator/ConcreteProduct pair, and nothing in the existing hierarchy has to change.
Question 10 of 10
When does Factory Method's own drawbacks say the pattern is pure overhead?
Correct! For a class that only ever needs to build one type of object, introducing a factory method is pure overhead with no payoff.
Not quite. For a class that only ever needs to build one type of object, introducing a factory method is pure overhead with no payoff.