Factory Method
Intent
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method defers instantiation to subclasses — the difference from simple factory is who decides: not a central switch, but each concrete creator subclass overriding one method.
The Problem Shape It Fits
A framework provides the workflow; applications provide the products. A dialog framework knows how to render a window with one button inside — but which button (Windows-styled, HTML, dark-mode)? The framework cannot hardcode it and cannot know all future button types. So it declares: “my code calls createButton(); whoever extends me implements that.”
CREATOR HIERARCHY PRODUCT HIERARCHY
┌──────────────────────┐ ┌──────────────┐
│ «abstract» Dialog │ creates │ «interface» │
│ + render() │────────────►│ Button │
│ # createButton() ◄───┼── declared └──────△───────┘
└──────────▲───────────┘ abstract │
│ overrides ├── WinButton
┌────────┴─────────┐ ├── HtmlButton
│ WindowsDialog │──── returns ───────────┤
│ createButton() │ new WinButton() ┆
├──────────────────┤ ┆
│ HtmlDialog │──── new HtmlButton() ┆
└──────────────────┘ ┆
The Dialog.render() skeleton calls its own abstract createButton() — a template method over creation. Subclasses never touch rendering logic; they only answer “what button?”
Mechanics
abstract class Dialog {
public void render() { // fixed workflow — closed for modification
Button b = createButton(); // ← the factory method hook
b.onClick(() -> close());
display(b);
}
protected abstract Button createButton(); // open for extension
}
class HtmlDialog extends Dialog {
@Override protected Button createButton() { return new HtmlButton(); }
}
Structure Roles
| Role | Job |
|---|---|
Product (Button) | Interface all products satisfy |
Creator (Dialog) | Declares factory method; uses product in business methods |
| Concrete creator | Overrides factory, returns its product |
| Concrete product | The actual class constructed |
Simple Factory vs Factory Method
| Simple factory | Factory method | |
|---|---|---|
| Decision location | One switch in one class | Distributed across subclasses |
| Adding a variant | Edit the switch | New subclass pair, zero edits |
| Inheritance involved | No | Yes |
| Fits | Closed variant sets | Frameworks, plugin extension |
Real-World Sightings
Calendar.getInstance()— locale decides the concrete calendar class.java.nio.charset.Charset.forName/ SLF4JLoggerFactory.getLogger— environment/config picks implementations.- JUnit’s runner discovery — framework asks, subclass/test answers.
Interview Framing
- The scoring sentence: “factory method is OCP applied to creation — the switch dissolves into the type system.”
- Follow-up trap: “why not just pass a
Supplier<Button>?” — valid modern alternative (composition instead of inheritance); knowing when either fits shows judgment.
The Scenario
A logistics app delivers by land and sea. Requirements grow quarterly (“add air freight, then drones”). The design must make each addition a new file, never an edit to delivery logic.
Full Implementation
// ---------- PRODUCT HIERARCHY ----------
interface Transport {
String deliver(String cargo);
}
class Truck implements Transport {
@Override public String deliver(String cargo) {
return "Truck: " + cargo + " via highway";
}
}
class Ship implements Transport {
@Override public String deliver(String cargo) {
return "Ship: " + cargo + " via sea lane";
}
}
// ---------- CREATOR HIERARCHY ----------
abstract class LogisticsManager {
// template method — the stable workflow (closed)
public final String planDelivery(String cargo) {
Transport t = createTransport(); // ← factory method hook
return t.deliver(cargo);
}
protected abstract Transport createTransport(); // open for extension
}
class RoadLogistics extends LogisticsManager {
@Override protected Transport createTransport() { return new Truck(); }
}
class SeaLogistics extends LogisticsManager {
@Override protected Transport createTransport() { return new Ship(); }
}
The Extension Proof
Air freight arrives next quarter:
class Plane implements Transport {
@Override public String deliver(String cargo) {
return "Plane: " + cargo + " via air";
}
}
class AirLogistics extends LogisticsManager {
@Override protected Transport createTransport() { return new Plane(); }
}
planDelivery, Truck, Ship — all untouched. Diff = two new files. That is OCP on creation, demonstrated.
The Testing Seam
class FakeTransport implements Transport {
@Override public String deliver(String cargo) { return "FAKE-OK"; }
}
class TestLogistics extends LogisticsManager {
@Override protected Transport createTransport() { return new FakeTransport(); }
}
// planDelivery now testable without any real transport side effects
The same hook that enables products enables fakes — creation points are always test seams.
Client Usage
Map<String, LogisticsManager> managers = Map.of(
"road", new RoadLogistics(),
"sea", new SeaLogistics());
managers.get(config.mode()).planDelivery("laptops");
One configuration line selects an entire behavior family.
Production Notes
- Constructors needing runtime data (API keys, region)? Pass through creator constructors — creators are normal objects.
- Expensive products? Creator can memoize (
if (transport == null) transport = create...) — caching belongs at this seam. - Modern alternative:
LogisticsManager(Supplier<Transport> t)collapses subclasses into lambdas; prefer it when no per-family logic beyond creation exists. Factory-method inheritance earns its keep only when creators carry additional behavior.
Interview Framing
- Being asked to implement it in 10 minutes is common; delivering product+creator hierarchies plus the fake-for-tests closes all rubric axes.
- Mentioning the
Suppliersimplification signals current-practice awareness without dismissing the classic form.
Premium Content
Unlock Factory Method and all premium lessons with a subscription.
From ₹199.99/year — See plans