Factory Method Pattern
Defines an interface for creating an object, but lets subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses, promoting loose coupling by eliminating the need to bind application-specific classes into the code.
✦ Problem
When code needs to create objects without specifying their exact classes, hardcoding constructors creates tight coupling. If you later add a new product type, you must modify every place that instantiates objects. This violates the open/closed principle and makes maintenance painful as the system grows.
✦ Solution
Replace direct object construction with calls to a special factory method. Objects returned by a factory method are often referred to as products. Subclasses override the factory method to change the class of products being created. The key constraint is that the products must share a common interface so the code that uses products can treat them uniformly.
Participants
| Role | Responsibility |
|---|---|
| Product | Declares the interface common to all objects the factory method creates |
| ConcreteProduct | Implements the Product interface |
| Creator | Declares the factory method returning a Product object. May define a default implementation |
| ConcreteCreator | Overrides the factory method to return a ConcreteProduct instance |
TypeScript Example
interface Transport {
deliver(): string;
}
class Truck implements Transport {
deliver(): string {
return "Delivering by road in a box";
}
}
class Ship implements Transport {
deliver(): string {
return "Delivering by sea in a container";
}
}
abstract class Logistics {
abstract createTransport(): Transport;
planDelivery(): string {
const transport = this.createTransport();
return transport.deliver();
}
}
class RoadLogistics extends Logistics {
createTransport(): Transport {
return new Truck();
}
}
class SeaLogistics extends Logistics {
createTransport(): Transport {
return new Ship();
}
}
const logistics: Logistics = new SeaLogistics();
console.log(logistics.planDelivery());
// "Delivering by sea in a container"Real-World Example
UI frameworks use the Factory Method pattern extensively. A dialog class may define a createButton() factory method. On Windows it returns a WindowsButton, on macOS a MacButton. The dialog code works with the abstract Button interface and never knows which concrete button it's using. This allows the framework to support new platforms without changing existing dialog code.
✓ When to Use
- •You don't know the exact types of objects your code will work with ahead of time
- •You want to let users extend the internal components of your library or framework
- •You want to reuse existing objects instead of rebuilding them each time to save resources
- •You need to decouple the product construction code from the code that uses the product
✗ When Not to Use
- •When there is only one type of product and no foreseeable need for extension
- •When the creation logic is trivial and adding an abstraction layer increases complexity without benefit
- •When product types are unlikely to change or grow
Understanding the Factory Method Pattern
The Factory Method pattern is one of the most important creational design patterns in object-oriented software design. Originally cataloged by the Gang of Four (Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides) in their seminal 1994 book "Design Patterns: Elements of Reusable Object-Oriented Software," this pattern continues to be a cornerstone of modern software architecture.
Defines an interface for creating an object, but lets subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses, promoting loose coupling by eliminating the need to bind application-specific classes into the code. The pattern involves the following key participants: Product, ConcreteProduct, Creator, ConcreteCreator. Each participant has a well-defined role, and the interactions between them create a flexible, maintainable structure that can evolve with changing requirements.
In TypeScript and modern JavaScript development, the Factory Method pattern is particularly valuable because the language's type system and interface support allow you to express the pattern with compile-time safety. TypeScript generics can make Factory Method implementations even more flexible and reusable while maintaining strong type checking throughout the pattern's structure.
Knowing when to apply the Factory Method pattern is as important as knowing how. Common use cases include: You don't know the exact types of objects your code will work with ahead of time; You want to let users extend the internal components of your library or framework; You want to reuse existing objects instead of rebuilding them each time to save resources. However, overusing design patterns can lead to unnecessarily complex code. Always evaluate whether the pattern genuinely solves your problem or if a simpler approach would suffice.
The Factory Method pattern is related to several other design patterns: Abstract Factory, Builder, Prototype, Template Method. Understanding these relationships helps you choose the right pattern for your specific situation and combine patterns effectively when building complex systems.
Related Patterns
Provides an interface for creating families of related or dependent objects without specifying their concrete classes. A…
Separates the construction of a complex object from its representation, allowing the same construction process to create…
Creates new objects by copying an existing object, known as the prototype. This pattern is useful when creating an insta…
Defines the skeleton of an algorithm in a method, deferring some steps to subclasses. Template Method lets subclasses re…
More Creational Patterns
Explore All Design Patterns
Browse our complete reference of 23 design patterns with TypeScript examples, UML participants, and practical guidance.