Factory Pattern in TypeScript
Imagine you’re building a car manufacturing plant. You don’t want to hardcode the production of a sedan, truck, or SUV in every part of your factory. Instead, you’d prefer a system where you can easily introduce new car types without modifying existing production lines. This is where the Factory Pattern shines in software engineering.
In TypeScript, the Factory Pattern is a creational design pattern that provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created. This pattern promotes loose coupling and makes your code more modular and easier to maintain.
🔹 Why It Matters
The Factory Pattern is crucial for several reasons:
- Decoupling: It separates object creation from the rest of the code, reducing dependencies.
- Scalability: Easily introduce new types without modifying existing code.
- Maintainability: Centralizes object creation logic, making it easier to manage.
- Testability: Facilitates unit testing by allowing mock objects to be injected.
🔹 Core Concepts
The Factory Pattern consists of:
- Creator (Factory): An interface or abstract class that declares the factory methods.
- Concrete Creators: Subclasses that implement the factory methods to create specific products.
- Product: The interface or abstract class for objects the factory method creates.
- Concrete Products: Classes that implement the Product interface.
Visual Aid: Factory Pattern Architecture
Source: Refactoring.Guru
🔹 Code Walkthrough
Let’s create a simple example of a vehicle factory in TypeScript.
Step 1: Define the Product Interface
// Vehicle.ts - Product Interface
interface Vehicle {
start(): void;
stop(): void;
}
Step 2: Implement Concrete Products
// Car.ts - Concrete Product
class Car implements Vehicle {
start() {
console.log("Car started");
}
stop() {
console.log("Car stopped");
}
}
// Bike.ts - Concrete Product
class Bike implements Vehicle {
start() {
console.log("Bike started");
}
stop() {
console.log("Bike stopped");
}
}
Step 3: Define the Creator Interface
// VehicleFactory.ts - Creator Interface
interface VehicleFactory {
createVehicle(type: string): Vehicle;
}
Step 4: Implement Concrete Creators
// ConcreteVehicleFactory.ts - Concrete Creator
class ConcreteVehicleFactory implements VehicleFactory {
createVehicle(type: string): Vehicle {
switch (type.toLowerCase()) {
case 'car':
return new Car();
case 'bike':
return new Bike();
default:
throw new Error('Unknown vehicle type');
}
}
}
Step 5: Use the Factory
// main.ts - Usage
const factory = new ConcreteVehicleFactory();
const car = factory.createVehicle('car');
car.start(); // Output: Car started
car.stop(); // Output: Car stopped
const bike = factory.createVehicle('bike');
bike.start(); // Output: Bike started
bike.stop(); // Output: Bike stopped
🔹 Common Mistakes
- Overusing Factories: Not every object creation needs a factory. Use it when it makes sense to decouple creation logic.
- Complex Hierarchies: Avoid creating overly complex hierarchies of factories and products.
- Ignoring Return Types: Ensure your factory methods return the correct type to avoid runtime errors.
🔹 Best Practices
- Keep It Simple: Start with a basic factory and expand as needed.
- Use Interfaces: Define clear interfaces for both the factory and the products.
- Leverage Dependency Injection: Pass factories as dependencies to make your code more flexible and testable.
- Document Your Code: Clearly document the purpose and usage of your factories and products.
🔹 Final Thoughts
The Factory Pattern is a powerful tool in your TypeScript arsenal, enabling you to write more flexible, scalable, and maintainable code. By decoupling object creation from the rest of your application, you can easily introduce new types and behaviors without modifying existing code.
For further reading, check out these resources:
- Design Patterns: Elements of Reusable Object-Oriented Software
- TypeScript Documentation
- Factory Method Pattern on Refactoring.Guru
By following this guide, you’ll be well-equipped to implement the Factory Pattern in your TypeScript projects, leading to cleaner, more maintainable code. Happy coding!