Factory Pattern
A creational design pattern that encapsulates object creation logic, allowing the client to request objects without knowing the concrete class or complex construction details.
Description
The Factory pattern encapsulates the logic for creating objects, decoupling the client code from the specific classes it needs to instantiate. Instead of calling new ConcreteClass() directly, the client calls a factory method or class that determines the correct type to create based on parameters, configuration, or runtime conditions. This enables the Open-Closed Principle: new types can be added by extending the factory without modifying client code.
The pattern comes in several variants. The Simple Factory is a function or static method that returns different types based on input: createNotification('email') returns EmailNotification while createNotification('sms') returns SmsNotification. The Factory Method defines an interface for creating objects but lets subclasses decide which class to instantiate. The Abstract Factory provides an interface for creating families of related objects—a UIFactory that produces Button, TextField, and Modal components styled consistently for a given theme or platform.
In TypeScript/JavaScript applications, factories are especially common for: creating database connections based on environment configuration, constructing complex objects with many optional parameters (builder pattern variant), instantiating strategy objects based on runtime conditions, and creating test fixtures with sensible defaults (factory-bot pattern). The factory pattern pairs naturally with dependency injection—a DI container is essentially a sophisticated factory that resolves dependency graphs automatically. The anti-pattern to avoid is the God Factory that creates everything in the application, violating the Single Responsibility Principle.
Prompt Snippet
Implement a PaymentProcessorFactory that returns the correct PaymentProcessor implementation (StripeProcessor, PayPalProcessor, BraintreeProcessor) based on the tenant's configuration stored in the database. Define the factory as a class registered in the tsyringe DI container that receives all processor implementations and a TenantConfigRepository. Use a Map<PaymentProvider, new (...args) => PaymentProcessor> for registration to ensure type safety and O(1) lookup. For tests, create a TestPaymentProcessorFactory that returns an InMemoryPaymentProcessor recording all calls for assertion. Include a factory method createFromConfig(config: TenantConfig): PaymentProcessor that validates the config and throws a descriptive DomainError if the requested provider is not registered.
Tags
Related Terms
Strategy Pattern
A behavioral design pattern that defines a family of interchangeable algorithms, encapsulates each one, and allows the algorithm to be selected at runtime.
Dependency Injection
A design pattern where objects receive their dependencies from an external source rather than creating them internally, enabling loose coupling and testability.
Repository Pattern
An abstraction layer that mediates between the domain/business logic and the data persistence layer, providing a collection-like interface for accessing domain objects.
Singleton (and why to avoid it)
A creational pattern that ensures a class has only one instance with a global access point, widely considered an anti-pattern due to hidden dependencies and testing difficulties.