Skip to content

Factory Method

Factory Method defines an interface for creating an object but lets each subclass decide which concrete class to instantiate, so the surrounding workflow stays the same while the product varies.

You have a process that is identical no matter what it operates on, except for one detail: the kind of object it works with. A logistics planner schedules deliveries the same way whether the cargo travels by truck or by ship — but it must produce the right kind of vehicle. If you hard-code new Truck() into the planner, supporting ships means editing the planner and risking the shared logic.

Factory Method extracts that single varying step into an overridable method. The base class writes the workflow in terms of an abstract product and calls its own factory method to obtain the concrete object. Subclasses override only the factory method. The workflow is inherited unchanged; the product is chosen by the subclass. This keeps the open/closed principle intact — you add a new product by adding a subclass, not by editing existing code.

classDiagram
  class Product {
    <<interface>>
    +deliver() string
  }
  class Truck {
    +deliver() string
  }
  class Ship {
    +deliver() string
  }
  class Logistics {
    +planDelivery() string
    +createTransport()* Product
  }
  class RoadLogistics {
    +createTransport() Product
  }
  class SeaLogistics {
    +createTransport() Product
  }
  Product <|.. Truck
  Product <|.. Ship
  Logistics <|-- RoadLogistics
  Logistics <|-- SeaLogistics
  Logistics ..> Product : creates
  RoadLogistics ..> Truck
  SeaLogistics ..> Ship
A Creator calls its own factory method; subclasses pick the Product
  • Product — the interface returned by the factory method. The workflow only ever depends on this type.
  • Concrete Products (Truck, Ship) — the specific implementations the workflow stays unaware of.
  • Creator (Logistics) — holds the shared workflow (planDelivery) and declares the abstract factory method (createTransport).
  • Concrete Creators (RoadLogistics, SeaLogistics) — override the factory method to return a particular product.

A Logistics planner whose planDelivery step is shared, while subclasses choose the transport.

interface Transport {
deliver(): string;
}
class Truck implements Transport {
deliver(): string {
return 'Delivering by road in a truck.';
}
}
class Ship implements Transport {
deliver(): string {
return 'Delivering by sea in a ship.';
}
}
abstract class Logistics {
// Shared workflow, written against the Transport interface.
planDelivery(): string {
const transport = this.createTransport();
return `Planned. ${transport.deliver()}`;
}
// The factory method subclasses override.
protected abstract createTransport(): Transport;
}
class RoadLogistics extends Logistics {
protected createTransport(): Transport {
return new Truck();
}
}
class SeaLogistics extends Logistics {
protected createTransport(): Transport {
return new Ship();
}
}
console.log(new RoadLogistics().planDelivery());
console.log(new SeaLogistics().planDelivery());
  • Pro: the shared workflow never names a concrete product, so adding a new product means adding a subclass, not editing callers.
  • Pro: product construction is in one overridable method, easy to find and replace.
  • Con: each new product variant tends to require a new creator subclass, which can multiply small classes.
  • Con: for a single product type it is overkill — a plain constructor or function is clearer.
  • Abstract Factory often uses several factory methods together to build a product family.
  • Prototype is an alternative when the new object should copy an existing one rather than be built fresh.
In Factory Method, who decides the concrete class to instantiate?
What stays the same across concrete creators?
Why does Factory Method support the open/closed principle?