Abstract Factory Pattern

Abstract Factory Pattern

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

Intent

The AbstractFactory interface declares creation methods for each distinct product type in the family. ConcreteFactories implement the interface, producing a coordinated set of ConcreteProducts. The Client works through the AbstractFactory and AbstractProduct interfaces — swapping one ConcreteFactory swaps the entire product family atomically. This guarantees that products from one family are always used together, eliminating cross-family mismatches (e.g., a Windows button with a Mac checkbox).

When NOT to Use

  • Only one product type needs to be created — use Factory Method instead
  • The product family is fixed and never changes — the factory interface adds indirection for no benefit
  • New product types must be added frequently — each new type requires changing every ConcreteFactory (the "open for extension" cost is high)
  • Products are simple value objects — a constructor is clearer

When to Use

  • System must be independent of how its products are created and composed
  • System is configured with one of multiple families (e.g., UI themes, database drivers, platform-specific widgets)
  • Products within a family must be used together — the factory enforces consistency (no mixing WinButton with MacCheckbox)

How It Works

Participants: AbstractFactory (declares createA(), createB()...), ConcreteFactory (implements all create methods for one family), AbstractProduct (interface per product type), ConcreteProduct (per family per type), Client (uses only abstract interfaces).

Swapping the factory object at runtime switches the entire product family. The client receives a factory at construction time (dependency injection) and never calls new directly on concrete products.

Class Diagram

classDiagram
    class AbstractFactory {
        <<interface>>
        +createProductA()* ProductA
        +createProductB()* ProductB
    }
    class ConcreteFactory1 {
        +createProductA() ProductA
        +createProductB() ProductB
    }
    class ConcreteFactory2 {
        +createProductA() ProductA
        +createProductB() ProductB
    }
    class ProductA {
        <<interface>>
    }
    class ProductB {
        <<interface>>
    }
    class ConcreteProductA1
    class ConcreteProductA2
    class ConcreteProductB1
    class ConcreteProductB2
    AbstractFactory <|.. ConcreteFactory1
    AbstractFactory <|.. ConcreteFactory2
    ProductA <|.. ConcreteProductA1
    ProductA <|.. ConcreteProductA2
    ProductB <|.. ConcreteProductB1
    ProductB <|.. ConcreteProductB2
    ConcreteFactory1 ..> ConcreteProductA1 : creates
    ConcreteFactory1 ..> ConcreteProductB1 : creates
    ConcreteFactory2 ..> ConcreteProductA2 : creates
    ConcreteFactory2 ..> ConcreteProductB2 : creates
    Client --> AbstractFactory

TypeScript Example

// Abstract Factory — TypeScript
interface Button { render(): void; }
interface Checkbox { render(): void; }
 
interface GUIFactory {
  createButton(): Button;
  createCheckbox(): Checkbox;
}
 
class WinButton implements Button {
  render() { console.log("Windows button"); }
}
class WinCheckbox implements Checkbox {
  render() { console.log("Windows checkbox"); }
}
 
class WinFactory implements GUIFactory {
  createButton(): Button { return new WinButton(); }
  createCheckbox(): Checkbox { return new WinCheckbox(); }
}
 
// Client works only through abstract interfaces
function renderUI(factory: GUIFactory) {
  factory.createButton().render();
  factory.createCheckbox().render();
}
renderUI(new WinFactory());

Java Example

// Abstract Factory — Java
interface Button { void render(); }
interface Checkbox { void render(); }
 
interface GUIFactory {
    Button createButton();
    Checkbox createCheckbox();
}
 
class WinButton implements Button {
    @Override public void render() { System.out.println("Windows button"); }
}
class WinCheckbox implements Checkbox {
    @Override public void render() { System.out.println("Windows checkbox"); }
}
 
class WinFactory implements GUIFactory {
    @Override public Button createButton() { return new WinButton(); }
    @Override public Checkbox createCheckbox() { return new WinCheckbox(); }
}

Confusion With

PatternKey Difference
Factory-Method-PatternFactory Method creates one product type via subclass override; Abstract Factory creates a coordinated family of products via a factory object with multiple creation methods
Static factory methodsCollections.unmodifiableList() — a static utility; not a GoF pattern, no family concept, no interface
Service LocatorService Locator retrieves existing instances; Abstract Factory creates new product instances
ConceptRelationship
Factory-Method-PatternAbstract Factory typically uses Factory Methods internally for each product type
Builder-PatternBuilder constructs one complex object step-by-step; Abstract Factory creates families in one call
Singleton-PatternConcreteFactory objects are often Singletons (prefer DI-managed single instance)
Prototype-PatternFactories can use Prototype to clone products rather than construct them

Sources

  • Gamma et al., Design Patterns, 1994, pp. 87–95 — authoritative definition and participants
  • refactoring.guru/design-patterns/abstract-factory — modern TypeScript framing