Factory Pattern β Factory Method & Abstract Factory (2026)
In this tutorial, you'll learn the three variations of the factory pattern β simple factory, factory method, and abstract factory β how each one solves object creation problems, and when to choose one over the other.
A car factory doesn't hand-assemble every vehicle from scratch. It has specialized assembly lines: one for sedans, one for SUVs, one for electric vehicles. Each line knows exactly how to build its type of car. You tell the factory "I need an SUV," and it routes your order to the right line. You don't build the car yourself β you delegate creation to the factory. In software, factory patterns do the same for object creation.
Core Concept
Factory patterns centralize object creation, hiding the instantiation logic from the client. They come in three levels of complexity:
| Variation | What It Does | Complexity |
|---|---|---|
| Simple Factory | A single function/class that creates objects based on input | Low |
| Factory Method | A method that subclasses override to create specific objects | Medium |
| Abstract Factory | An interface for creating families of related objects | High |
Simple Factory
A simple factory is a single function that returns different object types based on input:
// Simple Factory
function createLogger(type: "console" | "file" | "cloud"): Logger {
switch (type) {
case "console":
return new ConsoleLogger();
case "file":
return new FileLogger("/var/log/app.log");
case "cloud":
return new CloudLogger("log-storage-123");
default:
throw new Error(`Unknown logger type: ${type}`);
}
}
// Usage
const logger = createLogger("file");
logger.info("Application started");
Factory Method
The factory method pattern defines an interface for creating an object but lets subclasses decide which class to instantiate:
// Factory Method β base class with abstract factory method
abstract class DocumentExporter {
// The factory method β subclasses override this
protected abstract createExporter(): Exporter;
export(data: Record<string, unknown>): void {
const exporter = this.createExporter();
exporter.connect();
exporter.write(data);
exporter.close();
}
}
// Concrete creators override the factory method
class PdfExporter extends DocumentExporter {
protected createExporter(): Exporter {
return new PdfLibExporter();
}
}
class CsvExporter extends DocumentExporter {
protected createExporter(): Exporter {
return new CsvStreamExporter();
}
}
// Usage
const exporter: DocumentExporter = new PdfExporter();
exporter.export({ title: "Report", data: [1, 2, 3] });
// Uses PdfLibExporter internally β client doesn't know
Abstract Factory
The abstract factory pattern creates families of related objects without specifying their concrete classes:
// Abstract Factory interface
interface UIFactory {
createButton(): Button;
createTextInput(): TextInput;
createCheckbox(): Checkbox;
}
// Concrete factory for dark theme
class DarkThemeFactory implements UIFactory {
createButton(): Button {
return new DarkButton();
}
createTextInput(): TextInput {
return new DarkTextInput();
}
createCheckbox(): Checkbox {
return new DarkCheckbox();
}
}
// Concrete factory for light theme
class LightThemeFactory implements UIFactory {
createButton(): Button {
return new LightButton();
}
createTextInput(): TextInput {
return new LightTextInput();
}
createCheckbox(): Checkbox {
return new LightCheckbox();
}
}
// Usage β client only depends on the factory interface
function renderUI(factory: UIFactory) {
const button = factory.createButton();
const input = factory.createTextInput();
const checkbox = factory.createCheckbox();
// Render all components β they'll be consistently themed
}
ββββββββββββββββββββββββββββββββββββββββ
β Abstract Factory β
β + createButton(): Button β
β + createTextInput(): TextInput β
β + createCheckbox(): Checkbox β
ββββββββββββββββββββββββββββββββββββββββ
β β
ββββββββββββββββββββ ββββββββββββββββββββ
β DarkThemeFactory β βLightThemeFactory β
ββββββββββββββββββββ€ ββββββββββββββββββββ€
β createButton() β β createButton() β
β createTextInput()β β createTextInput()β
β createCheckbox() β β createCheckbox() β
ββββββββββββββββββββ ββββββββββββββββββββ
Expected output: The client gets a consistent set of UI components without knowing which theme is active. Switching themes means passing a different factory β zero changes to rendering code.
Real-World Examples
Database Driver Selection
DodaZIP uses an abstract factory to support different storage backends. The StorageFactory interface defines methods for createReader, createWriter, and createLister. Concrete factories (S3StorageFactory, LocalStorageFactory, GCSStorageFactory) implement each method. Adding a new backend means writing one new factory class.
Cross-Platform UI
Abstract factory is the foundation of cross-platform UI toolkits. A WidgetFactory creates buttons, windows, and menus. The WindowsFactory returns Windows-native widgets. The MacFactory returns macOS-native widgets. The same application code works on both platforms.
Factory vs Dependency Injection
| Aspect | Factory | Dependency Injection |
|---|---|---|
| Purpose | Decides which object to create | Decides how to wire objects together |
| Control flow | Client asks factory for an object | Container injects dependencies automatically |
| Complexity | Simple to implement | Requires container setup |
| Best for | Object creation with conditional logic | Wiring complex dependency graphs |
They're not mutually exclusive β a factory can be registered with a DI container.
Pros & Cons
| Pros | Cons |
|---|---|
| Centralizes object creation logic | Adds complexity for simple object creation |
| Follows Open/Closed Principle | Abstract factory adds many interfaces |
| Hides concrete classes from clients | Can lead to class explosion |
| Enforces consistency across related objects | Overkill for objects with no variation |
When to Use
Use the simple factory when you have one creation point with conditional logic. Use factory method when subclasses need to control which objects they create. Use abstract factory when you need to create families of related objects that must be consistent.
Skip factories entirely when a simple new statement suffices, the object has no variants, or a DI container handles creation naturally.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro