Back to Design Patterns
Creational Pattern

Abstract Factory Pattern

Provide an interface for creating families of related objects without specifying their concrete classes — ensuring products from the same family are always used together.

Intermediate11 min read

What is the Abstract Factory Pattern?

The Abstract Factory pattern is a creational design pattern that lets you create families of related objects through a single interface — without specifying the concrete classes of the objects you're creating.

Think of it as a furniture showroom: you pick a style (Modern, Victorian, Art Deco). Each style gives you a matching sofa, chair, and table. You'd never mix a Victorian sofa with a Modern chair — the factory ensures every product belongs to the same family.

In software, this prevents mismatches like rendering a macOS button next to a Windows checkbox. The factory acts as the consistency contract: everything it creates is guaranteed to work together.

Key Idea: Create families of related objects through one interface. Swap the entire family by swapping the factory — mix-and-match between families is impossible by design.

Abstract Factory vs Factory Method

These two patterns are often confused. Here's the key difference:

AspectAbstract FactoryFactory Method
CreatesA family of related objectsOne type of object
InterfaceMultiple creation methodsOne creation method
ConsistencyProducts guaranteed compatibleNo such guarantee
MechanismComposition — inject the factoryInheritance — subclass overrides
Use whenMultiple related products vary togetherOne product type varies

Rule of thumb: If you find yourself needing a Factory Method for every product in a family, and all those factories need to be consistent — you need Abstract Factory.

Core Participants

Abstract Factory

Abstract Factory Interface

Declares creation methods for each distinct product type in the family (e.g., createButton(), createCheckbox()). All concrete factories implement this interface.

Concrete Factory

Concrete Factory

Implements the abstract factory interface for a specific product family (e.g., WindowsUIFactory, MacUIFactory). Creates only products from its own family.

Abstract Product

Abstract Product

Interface for each distinct type of product (Button, Checkbox, TextInput). Concrete products implement these interfaces.

Concrete Product

Concrete Product

The real objects created by a concrete factory (WindowsButton, MacButton). Products from the same factory are guaranteed to be compatible with each other.

Client

Client

Uses only the abstract factory and abstract product interfaces. Never refers to concrete classes — the factory produces all objects, ensuring consistency.

!Problem It Solves

Incompatible Product Families

Without Abstract Factory, client code uses concrete classes directly. Switching from Windows to macOS UI means finding and replacing every construction call. Worse — there's nothing stopping you from accidentally mixing components from different families.

// Without Abstract Factory — scattered concrete references
Button btn = new WindowsButton();    // hardcoded
Checkbox cb = new MacCheckbox();     // oops — wrong family!
// Nothing prevents this mismatch. Bugs guaranteed.

Solution With Abstract Factory

// With Abstract Factory — factory guarantees family consistency
UIComponentFactory factory = new WindowsUIFactory(); // or MacUIFactory
Button btn      = factory.createButton();    // always Windows
Checkbox cb     = factory.createCheckbox();  // always Windows — guaranteed!
// Swap factory → entire family switches atomically

Implementation

// Abstract Factory Pattern — Cross-Platform UI Components

// ─── Abstract Products ────────────────────────────────────────
interface Button {
    void render();
    void onClick();
}

interface Checkbox {
    void render();
    void toggle();
}

interface TextInput {
    void render();
    String getValue();
}

// ─── Concrete Products: Windows ───────────────────────────────
class WindowsButton implements Button {
    public void render()  { System.out.println("[Windows] Rendering flat button with accent color"); }
    public void onClick() { System.out.println("[Windows] Button click — ripple effect"); }
}

class WindowsCheckbox implements Checkbox {
    public void render()  { System.out.println("[Windows] Rendering square checkbox"); }
    public void toggle()  { System.out.println("[Windows] Checkbox toggled — checkmark animates"); }
}

class WindowsTextInput implements TextInput {
    public void render()     { System.out.println("[Windows] Rendering underline text field"); }
    public String getValue() { return "Windows input value"; }
}

// ─── Concrete Products: macOS ─────────────────────────────────
class MacButton implements Button {
    public void render()  { System.out.println("[macOS] Rendering pill-shaped button"); }
    public void onClick() { System.out.println("[macOS] Button click — subtle shadow"); }
}

class MacCheckbox implements Checkbox {
    public void render()  { System.out.println("[macOS] Rendering rounded checkbox"); }
    public void toggle()  { System.out.println("[macOS] Checkbox toggled — smooth slide"); }
}

class MacTextInput implements TextInput {
    public void render()     { System.out.println("[macOS] Rendering rounded bordered field"); }
    public String getValue() { return "macOS input value"; }
}

// ─── Abstract Factory ─────────────────────────────────────────
interface UIComponentFactory {
    Button createButton();
    Checkbox createCheckbox();
    TextInput createTextInput();
}

// ─── Concrete Factories ───────────────────────────────────────
class WindowsUIFactory implements UIComponentFactory {
    public Button    createButton()    { return new WindowsButton(); }
    public Checkbox  createCheckbox()  { return new WindowsCheckbox(); }
    public TextInput createTextInput() { return new WindowsTextInput(); }
}

class MacUIFactory implements UIComponentFactory {
    public Button    createButton()    { return new MacButton(); }
    public Checkbox  createCheckbox()  { return new MacCheckbox(); }
    public TextInput createTextInput() { return new MacTextInput(); }
}

// ─── Client — uses factory without knowing concrete types ──────
class LoginForm {
    private Button submitBtn;
    private Checkbox rememberMe;
    private TextInput emailInput;

    public LoginForm(UIComponentFactory factory) {
        submitBtn  = factory.createButton();
        rememberMe = factory.createCheckbox();
        emailInput = factory.createTextInput();
    }

    public void render() {
        System.out.println("\n--- Rendering Login Form ---");
        emailInput.render();
        rememberMe.render();
        submitBtn.render();
    }

    public void submit() {
        submitBtn.onClick();
        System.out.println("Email: " + emailInput.getValue());
    }
}

// ─── Entry point ──────────────────────────────────────────────
public class App {
    public static void main(String[] args) {
        String os = System.getProperty("os.name").toLowerCase();

        UIComponentFactory factory = os.contains("mac")
            ? new MacUIFactory()
            : new WindowsUIFactory();

        LoginForm form = new LoginForm(factory);
        form.render();
        form.submit();
        // All components are guaranteed to match the same platform!
    }
}

When to Use Abstract Factory

Cross-Platform UI Toolkits

When your app must run on multiple platforms (Windows, macOS, Web) and each platform needs its own look-and-feel for every component.

WindowsUIFactory, MacUIFactory, WebUIFactory

Multi-Environment Infrastructure

When switching environments (local, staging, production) should swap the entire infrastructure family — database, cache, and message queue together.

LocalDevFactory, AWSFactory, GCPFactory

Theme / Skin Systems

When the entire UI must switch coherently between themes (light/dark, brand A/brand B) and partial swaps are unacceptable.

LightThemeFactory, DarkThemeFactory

Database Driver Families

When query builders, connection objects, and result parsers must all match the same database vendor.

MySQLFactory, PostgresFactory, SQLiteFactory

Pros & Cons

Advantages

  • Products from the same factory are guaranteed compatible
  • Swap the entire product family by swapping one factory
  • Client code is decoupled from all concrete product classes
  • Follows Open/Closed — add new families without changing client
  • Single place to change the product family used throughout the app

Disadvantages

  • Adding a new product type requires changing all factory interfaces and implementations
  • More classes and interfaces — higher initial complexity
  • Overkill for simple object creation with no family constraint
  • Hard to support partial overrides between families

Real-World Examples

Java AWT / Swing Look and Feel

Java's Swing uses Abstract Factory to support multiple look-and-feels (Metal, Nimbus, Windows). Calling UIManager.setLookAndFeel() swaps the factory — all components re-render in the new style.

Doctrine DBAL (PHP)

Doctrine's database abstraction layer uses Abstract Factory to create platform-specific query builders, schema managers, and result parsers — all matched to the connected database vendor.

React Native StyleSheet

React Native's platform-specific rendering uses an Abstract Factory concept — the same component tree produces native iOS UIKit views or Android View objects depending on the platform factory.

Spring Boot Auto-Configuration

Spring Boot's conditional beans (@ConditionalOnClass, @Profile) act as Abstract Factories — the right family of beans (datasource, cache, messaging) is wired together based on the active environment profile.

What's Next?

Explore other Creational patterns: