designpattern.site

Abstract Factory Pattern

Provide an interface for creating families of related objects without specifying their concrete classes.

Overview

The Abstract Factory pattern is a creational design pattern that provides an interface for producing families of related objects. The key word is family: every product that a factory creates is designed to work together. A WindowsFactory produces WindowsButton and WindowsCheckbox — objects that are visually and functionally consistent with each other.

Think of it as a factory of factories. Instead of calling new WindowsButton() directly, your application asks a factory object for a button. Swap the factory, and you get a completely different family of objects — no conditional code required.

Abstract Factory is closely related to the Factory Method pattern, but it operates at a higher level of abstraction. Where Factory Method handles a single product, Abstract Factory coordinates the creation of an entire product suite.

The Problem

Imagine you are building a cross-platform desktop application. It needs native-looking UI widgets — buttons, checkboxes, text fields — on Windows, macOS, and Linux. The naive approach is to scatter if (platform === 'windows') checks throughout your rendering code.

This approach breaks down quickly. Every new widget type requires touching every platform branch. Every new platform requires editing code that was already working. And widget objects from different platforms may accidentally be mixed together, producing a visually inconsistent UI.

What you need is a way to guarantee that all widgets created for a given platform come from the same source — and that swapping platforms is a single, localised change.

The Solution

Define an abstract factory interface that declares a creation method for each product type in the family. Provide one concrete factory per product family. Client code receives a factory object and calls its methods — it never instantiates concrete products directly.

The client is decoupled from the concrete classes. Adding a new theme or platform means adding a new factory and a new set of product classes, without touching any existing client code.

Abstract Factory vs Factory Method

Factory Method uses inheritance: a subclass overrides one method to create one product. Abstract Factory uses composition: a factory object holds multiple creation methods, one per product type in the family. Abstract Factory often uses Factory Method internally for each product it creates.

Class Diagram

Loading diagram...

The GUIFactory interface declares two factory methods. WindowsFactory and MacFactory each implement the interface and return platform-specific products. The Application class works only against the GUIFactory interface, so it is unaware of which concrete factory or products it is using.

Implementation

The example below models a UI theme system with a LightThemeFactory and DarkThemeFactory. Each factory creates a matching Button and TextField. The renderUI function works against the abstract factory interface and is completely theme-agnostic.

// Product interfaces
interface Button {
  render(): string;
}

interface TextField {
  render(): string;
}

// Abstract factory interface
interface ThemeFactory {
  createButton(): Button;
  createTextField(): TextField;
}

// Light theme products
class LightButton implements Button {
  render(): string {
    return "[Light Button]";
  }
}

class LightTextField implements TextField {
  render(): string {
    return "[Light TextField]";
  }
}

// Dark theme products
class DarkButton implements Button {
  render(): string {
    return "[Dark Button]";
  }
}

class DarkTextField implements TextField {
  render(): string {
    return "[Dark TextField]";
  }
}

// Concrete factories
class LightThemeFactory implements ThemeFactory {
  createButton(): Button {
    return new LightButton();
  }
  createTextField(): TextField {
    return new LightTextField();
  }
}

class DarkThemeFactory implements ThemeFactory {
  createButton(): Button {
    return new DarkButton();
  }
  createTextField(): TextField {
    return new DarkTextField();
  }
}

// Client code — works with any ThemeFactory
function renderUI(factory: ThemeFactory): void {
  const button = factory.createButton();
  const textField = factory.createTextField();
  console.log(button.render());
  console.log(textField.render());
}

// Usage
renderUI(new LightThemeFactory());
// [Light Button]
// [Light TextField]

renderUI(new DarkThemeFactory());
// [Dark Button]
// [Dark TextField]

Real-World Examples

Cross-platform UI toolkits. Qt and wxWidgets use Abstract Factory internally to dispatch widget creation to platform-specific backends. Your application code calls createButton() on an abstract factory; the framework supplies a Win32Factory, CocoaFactory, or GTKFactory depending on the host OS.

Database access layers. An ORM or query builder can expose a factory that creates dialect-specific Connection, QueryBuilder, and SchemaManager objects. Switching from PostgreSQL to MySQL becomes a matter of swapping the factory, not rewriting queries.

Cloud provider SDKs. Infrastructure-as-code tools model provider families through abstract factories. A CloudFactory yields a consistent set of StorageClient, ComputeClient, and DatabaseClient objects. The same provisioning logic works against AWS, GCP, or Azure by changing one factory reference.

Theme and skin systems. Desktop applications and design systems use Abstract Factory to manage visual themes. A ThemeFactory creates coordinated sets of colours, typography, and component styles. Changing the active theme replaces one factory object, and every component automatically receives matching visuals.

Pros and Cons

Pros

  • Products from the same factory are guaranteed to be compatible with each other.
  • Concrete product classes are hidden from client code, reducing coupling.
  • Adding a new product family (e.g., a new platform or theme) requires only new classes, not changes to existing code — open/closed principle in practice.
  • Centralising product creation makes it easy to enforce invariants across a family.

Cons

  • Adding a new product type to the family requires updating the abstract factory interface and every concrete factory — a potentially wide change.
  • The pattern introduces a significant number of classes and interfaces. For simple scenarios, this overhead is rarely justified.
  • Factory objects are often singletons or application-wide dependencies, which can make testing harder if not combined with dependency injection.

When to Use

  • Your system needs to work with multiple families of related products and you want to guarantee family consistency.
  • You want to isolate client code from the concrete classes it uses.
  • You are building a framework or library that your consumers will extend with their own product families.

When to Avoid

  • You only have one product family and that is unlikely to change — Factory Method or a simple constructor is enough.
  • The product families are stable but the set of product types within each family changes often — each addition forces changes across all factories.
  • The added abstraction makes the code harder to read without a clear benefit for the scale of your project.

Factory Method is commonly used inside concrete factories to create individual products. Abstract Factory is the higher-level pattern; Factory Method is often how it does the work.

Builder also assembles complex objects step by step, but it focuses on the construction process rather than which family the product belongs to. Use Builder when the construction logic is complex; use Abstract Factory when you need family consistency.

Prototype can be used as an alternative to Abstract Factory when product families should be created by cloning pre-configured prototype objects rather than instantiating new classes.

On this page