Factory Method
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
Intent
Define a method in a base class that returns an object conforming to a common interface, but defer the decision of which concrete class to instantiate to subclasses. This allows a framework to work with any user-defined product type without modification.
Problem
A class needs to create objects but cannot anticipate the exact type of object it must create. Hard-coding a specific class name couples the creator to that particular product, making it impossible to extend the system with new product types without modifying existing code. You need a way to delegate the 'which class?' decision to a point that can be overridden.
Solution
Replace direct constructor calls with calls to a special factory method. The base class declares the factory method (often abstract), and each subclass overrides it to return a different product variant. Client code calls the factory method through the base class interface, remaining decoupled from concrete product types.
Participants
- Creator — declares the factory method (which may have a default implementation) and uses it to obtain product instances
- ConcreteCreator — overrides the factory method to return a specific ConcreteProduct
- Product — defines the interface of objects the factory method creates
- ConcreteProduct — implements the Product interface
Advantages
- Decouples the creator from concrete product classes (Dependency Inversion Principle)
- New product types can be introduced without changing existing creator code (Open/Closed Principle)
- Centralizes product creation logic in one place, making it easier to control and swap implementations
- Supports the 'programming to an interface' principle naturally
Disadvantages
- Requires creating a new subclass of the creator for each new product type, which can lead to a parallel class hierarchy
- Adds indirection that can make the code harder to follow for simple cases
- If the creator has significant logic beyond product creation, subclassing just for the factory method can feel heavyweight
Real-World Analogy
A logistics company has a central dispatch office (creator) that schedules deliveries. The office does not decide whether to use a truck or a ship — that decision is made by regional suboffices (concrete creators) that know local conditions. The dispatch office only knows that it will get a 'transport' that can deliver cargo. Rural suboffices return trucks; coastal suboffices return ships.
Use Cases
- Framework code that lets application-level subclasses control which objects to create
- Document editors where each application type (text editor, spreadsheet) creates its own document subclass
- Logistics systems where the transport type is determined by region or cargo type
- Plugin systems where third-party modules register their own creators
- Notification services that produce email, SMS, or push notifications depending on configuration
Code Examples
// --- Product interface ---
interface Transport {
deliver(cargo: string): string;
capacity(): number;
}
// --- Concrete products ---
class Truck implements Transport {
deliver(cargo: string) {
return `🚚 Delivering "${cargo}" by road`;
}
capacity() { return 10; } // tons
}
class Ship implements Transport {
deliver(cargo: string) {
return `🚢 Delivering "${cargo}" by sea`;
}
capacity() { return 500; } // tons
}
// --- Creator ---
abstract class LogisticsCompany {
/** Factory method — subclasses decide which Transport to create */
abstract createTransport(): Transport;
/** Shared business logic that uses the factory method */
planDelivery(cargo: string): string {
const transport = this.createTransport();
return [
`Capacity: ${transport.capacity()} tons`,
transport.deliver(cargo),
].join("\n");
}
}
// --- Concrete creators ---
class RoadLogistics extends LogisticsCompany {
createTransport(): Transport {
return new Truck();
}
}
class SeaLogistics extends LogisticsCompany {
createTransport(): Transport {
return new Ship();
}
}
// --- Client code works with the base class ---
function ship(company: LogisticsCompany, cargo: string) {
console.log(company.planDelivery(cargo));
}
ship(new RoadLogistics(), "Electronics");
ship(new SeaLogistics(), "Grain");Abstract creator class for a logistics system. Each concrete creator overrides the factory method to return the appropriate transport type, while shared planning logic stays in the base class.