Low-level design2 min
Factory Method
Type: Creational Pattern
Relevance: Very High. Widely used in frameworks and libraries.
The Factory Method pattern defines an interface for creating an object, but lets subclasses alter the type of objects that will be created. It delegates the instantiation logic to child classes.
Real-Life Analogy
Think of a Logistics Company. Initially, the company only handles road transportation. Their primary delivery vehicle is a Truck.
Later, the company expands to sea transportation. If the core logic directly creates new Truck() everywhere, you have to rewrite the entire system.
Instead, the company creates a general TransportFactory. The RoadLogistics subclass creates Truck objects, and the SeaLogistics subclass creates Ship objects.
The Problem
Imagine building a UI framework. You have a Button interface, and you want to render a WindowsButton on Windows and an HTMLButton on the web.
If your main application code has new WindowsButton() hardcoded everywhere, porting it to the web is going to be incredibly painful.
How to Implement Factory Method
- Define an interface for the product being created (e.g.,
Transport). - Create a creator abstract class with an abstract
createTransport()method. - Subclass the creator and implement the factory method to return concrete products.
Example in Code
// 1. The Product Interface
interface Transport {
deliver(): void;
}
// Concrete Products
class Truck implements Transport {
deliver(): void { console.log("Delivering by land in a box."); }
}
class Ship implements Transport {
deliver(): void { console.log("Delivering by sea in a container."); }
}
// 2. The Creator (Abstract)
abstract class Logistics {
// The Factory Method
abstract createTransport(): Transport;
// Core business logic that uses the factory method
public planDelivery(): void {
const transport = this.createTransport();
console.log("Logistics: Planning delivery route...");
transport.deliver();
}
}
// 3. Concrete Creators
class RoadLogistics extends Logistics {
createTransport(): Transport {
return new Truck();
}
}
class SeaLogistics extends Logistics {
createTransport(): Transport {
return new Ship();
}
}
// Usage
const appType = "SEA";
let logistics: Logistics;
if (appType === "SEA") {
logistics = new SeaLogistics();
} else {
logistics = new RoadLogistics();
}
// The client doesn't know or care whether a Ship or Truck is created!
logistics.planDelivery();
Class Diagram
Why is it Important?
- Single Responsibility Principle: You move the product creation code into one place in the program.
- Open/Closed Principle: You can introduce new types of products into the program without breaking existing client code (just add a new subclass).