Skip to content

Factory Pattern β€” Factory Method & Abstract Factory (2026)

DodaTech Updated 2026-06-20 6 min read

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

What's the difference between Factory Method and Abstract Factory?

Factory Method is a single method that subclasses override to create one type of object. Abstract Factory is an interface with multiple related factory methods that create a family of objects. Factory Method inherits behavior; Abstract Factory composes behaviors.

Should I always use a factory instead of `new`?

No. Use new directly when the object is simple, unlikely to change, and doesn't need conditional creation logic. Add a factory when you anticipate alternative implementations or when creation requires configuration.

Does the factory pattern replace Dependency Injection?

No. DI handles how objects are wired together. A factory handles how objects are created. You can inject a factory into a class so the class can create objects on demand without knowing the concrete type.

Can I combine factory with the Singleton Pattern?

Yes β€” factories are often made singletons so there's one creation point for the application. Just ensure the factory doesn't hold state that would make testing difficult.

How do I avoid switch statements in factories?

Replace switch statements with a registry pattern: concrete product classes register themselves with the factory at startup. The factory maintains a map of type β†’ class. Adding a new product means adding a new registration line β€” no switch modification

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro