What is the Factory Method design pattern, and what problem does it solve?
answer
- creation behind an overridable hook
- creator never writes `new Concrete`
- subtype decides the concrete product
- parallel Creator/Product hierarchies
- Template Method applied to instantiation
basics
~20 sFactory Method hides object creation behind an overridable method. Code that needs an object calls that method instead of naming a concrete class directly, and a subtype (or implementation) decides which concrete class actually gets built.
solid answer
~50 sFactory Method defines an interface (a method) for creating an object, but lets subtypes decide which concrete class to instantiate — instantiation is deferred away from the code that uses the object. A "creator" type contains business logic that needs a "product"; instead of writing `new ConcreteProduct()` inline, it calls its own `createProduct()` method, which returns the product's abstract type. Concrete creators override that method to return different concrete products. The payoff is that the creator's algorithm is written once against the product interface and never mentions a concrete class, so adding a new product means adding a new creator/override rather than editing existing logic. Costs: it introduces a parallel hierarchy (one creator per product family branch) and can be overkill when a single parameterized function would do. It is the smallest of the creational patterns and the building block that Abstract Factory generalizes.
code
pseudocode · 19 linesabstract class Logistics { // Creator
abstract fun createTransport(): Transport // factory method
fun planDelivery(order) { // logic written once, no `new Concrete`
val t = createTransport()
t.load(order); t.deliver()
}
}
class RoadLogistics : Logistics {
override fun createTransport() = Truck() // ConcreteCreator picks the product
}
class SeaLogistics : Logistics {
override fun createTransport() = Ship()
}
// caller decides ONCE, at wiring time:
val logistics = if (config.overseas) SeaLogistics() else RoadLogistics()
logistics.planDelivery(order)go deeper
State the intent in one sentence (creation behind an overridable method, subtype picks the class) and give the classic Transport/Truck/Ship example.
Name the four roles, explain that the creator also holds business logic, and mention the decoupling benefit plus the parallel-hierarchy cost.
Connect it to Dependency Inversion and Open/Closed, discuss inheritance-based vs. delegation-based factories, and say when a plain function or DI is the better choice.
Frame it as a variation point in the architecture: where the wiring decision lives, how it interacts with DI containers, plugin registries, and configuration, and the maintenance cost of speculative variation points.
## The problem When code writes `new ConcreteThing()` directly, it becomes **coupled to that exact class**. Every place that does so must change if you later need a different implementation, and the calling code can no longer be reused for another variant. Compile-time coupling to a constructor is the hardest kind to swap out, because a constructor call names a class and returns exactly that class — it cannot be overridden, substituted, or extended. ## The pattern **Factory Method** (from the classic "Gang of Four" catalogue of design patterns) says: > Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. Four roles: | Role | Meaning | |---|---| | **Product** | The abstract type (interface / abstract class) the caller programs against, e.g. `Transport`. | | **ConcreteProduct** | A specific implementation, e.g. `Truck`, `Ship`. | | **Creator** | The type that *needs* a product and contains logic using it. It declares the factory method, e.g. `createTransport(): Transport`, and calls it from its own code. It never names a ConcreteProduct. | | **ConcreteCreator** | Overrides the factory method to return a specific ConcreteProduct. | The crucial, often-missed detail: the **Creator is usually not just a factory**. Its main job is the business logic (`planDelivery()`), and the factory method is a *hook* inside it. That is why the pattern is a form of the **Template Method** idea applied to creation — the base class controls the algorithm and delegates one varying step ("which object do I build?") to subtypes. ## Mechanism, step by step 1. Caller holds a `Creator` reference (which concrete creator it is was decided once, at wiring/configuration time). 2. Caller invokes `creator.planDelivery()`. 3. Inside, the creator calls `this.createTransport()` — a virtual/overridable call, so **dynamic dispatch** picks the subtype's implementation at runtime. 4. The subtype returns a `Truck` or `Ship`, but statically typed as `Transport`. 5. The rest of `planDelivery()` uses only the `Transport` interface, so it works unchanged with any product. So the *decision* about the concrete class moves from the point of use to the point of configuration (which creator you instantiated), which is typically a single place at the edge of the system. ## Variations that still count as Factory Method - **Default implementation**: the base creator's factory method returns a sensible default product and subtypes override only when they need something else. - **Parameterized factory method**: `createTransport(kind)` with a switch inside — pragmatic, common, but it re-centralizes the decision and weakens extensibility (you must edit the switch to add a product). - **Delegation instead of inheritance**: the creator receives a `TransportFactory` collaborator and calls `factory.create()`. Many people call this Factory Method too; purists call it *Strategy applied to creation* or a *factory object*. It achieves the same decoupling without a parallel class hierarchy and is generally friendlier to dependency injection. ## Trade-offs **Benefits** - Removes `new Concrete` from business logic → the logic depends only on abstractions (Dependency Inversion). - New product ⇒ new creator subtype; existing, tested code is untouched (Open/Closed). - Creation logic (validation, pooling, caching, choosing an implementation by environment) lives in one overridable place. - Tests can supply a creator that returns a fake/stub product. **Costs** - **Parallel hierarchies**: each new product branch tends to drag a matching creator subclass along, doubling class count. - Indirection makes "what actually gets created here?" harder to answer by reading code. - Inheritance-based creation ties you to subclassing, which is a rigid extension mechanism; a factory *function* or DI container is often simpler. - Pointless when there is exactly one implementation and no realistic prospect of a second — that is speculative generality. ## Edge cases worth knowing - **Return type covariance**: some languages let a subtype narrow the return type (`Truck` instead of `Transport`). Useful, but callers that rely on the narrowed type re-introduce coupling. - **Calling a factory method from a constructor** is dangerous in languages with virtual dispatch during construction (the subtype's override may run before the subtype's fields are initialized). Prefer lazy creation in a normal method. - **Object lifetime**: the factory method is a natural place to decide *new instance vs. cached/shared instance*; callers cannot tell the difference, which is a feature (and a hazard if they assume uniqueness or assume sharing).
- Where does the decision about the concrete class actually happen if the business logic no longer names it?It moves to the composition/wiring edge of the system — whichever code picks the concrete creator (startup configuration, a DI container, a feature flag, a plugin registry). The pattern doesn't delete the decision, it relocates it to one place.
- Does the caller of the factory method ever see the concrete product type?Normally no — the method's declared return type is the abstract product, so the caller compiles against the interface. Some languages allow covariant return types so a subtype can advertise the concrete type, but relying on that re-couples the caller.
A logistics company's dispatcher always runs the same delivery plan: pick up, transport, drop off. The plan says "get a vehicle" without saying which. The road branch's dispatcher hands over a truck, the sea branch's hands over a ship. The plan text never changes; only the branch decides the vehicle.
saying these in an interview costs you the question
- Saying "any method that returns a new object is the Factory Method pattern" — a plain helper that returns objects is a factory, not necessarily this pattern.
- Claiming the factory method must be static; in the GoF form it must be overridable, and static methods cannot be overridden.
- Believing the pattern removes the choice of concrete class — it only moves the choice out of business logic into configuration/wiring.
- Thinking the creator's only job is creating; usually it also holds the algorithm that consumes the product.
- Assuming Factory Method and Abstract Factory are the same thing.