Abstract Factory
Some objects don't just need to be created correctly on their own — they need to be created consistently with each other. A light-themed button next to a dark-themed checkbox looks broken even though both components work perfectly in isolation. The Abstract Factory pattern solves this by producing a whole family of related objects through one shared interface, guaranteeing that whatever gets created together actually belongs together.
Abstract Factory vs. Factory Method: The Key Difference
| Factory Method | Abstract Factory |
|---|---|
Creates one type of product through a single creation method
(e.g. create($type) returning one Notification). |
Creates a whole family of related products through several creation methods
on one interface (e.g. createButton() AND createCheckbox()). |
| Answers: "which concrete class of this ONE thing do I build?" | Answers: "which concrete SET of matching things do I build?" |
This pair is probably the most commonly confused in the whole GoF catalogue, and in practice an Abstract Factory is often implemented using several Factory Methods internally — one per product it knows how to create. The distinction is about scope: one product vs. a coordinated family.
A Worked Example: A UI Theme System
Imagine building a UI toolkit that supports both a light theme and a dark theme. Every themed component — buttons, checkboxes — must come from the same theme, or the UI looks inconsistent. First, define the product interfaces and a factory interface that can build one of each:
<?php
interface Button {
public function render(): string;
}
interface Checkbox {
public function render(): string;
}
// The Abstract Factory interface: one method PER product in the family
interface ThemeFactory {
public function createButton(): Button;
public function createCheckbox(): Checkbox;
}
Now the concrete light-theme family, all styled consistently:
class LightButton implements Button {
public function render(): string {
return '<button class="btn-light">OK</button>';
}
}
class LightCheckbox implements Checkbox {
public function render(): string {
return '<input type="checkbox" class="chk-light">';
}
}
class LightThemeFactory implements ThemeFactory {
public function createButton(): Button {
return new LightButton();
}
public function createCheckbox(): Checkbox {
return new LightCheckbox();
}
}
And the dark-theme family, mirroring it exactly:
class DarkButton implements Button {
public function render(): string {
return '<button class="btn-dark">OK</button>';
}
}
class DarkCheckbox implements Checkbox {
public function render(): string {
return '<input type="checkbox" class="chk-dark">';
}
}
class DarkThemeFactory implements ThemeFactory {
public function createButton(): Button {
return new DarkButton();
}
public function createCheckbox(): Checkbox {
return new DarkCheckbox();
}
}
Client code depends only on ThemeFactory — swap the whole family by swapping one object:
function renderSettingsPanel(ThemeFactory $factory): string {
$button = $factory->createButton();
$checkbox = $factory->createCheckbox();
return $checkbox->render() . $button->render();
}
// Pick ONE factory based on the user's preference — every component that follows matches
$factory = $userPrefersDarkMode ? new DarkThemeFactory() : new LightThemeFactory();
echo renderSettingsPanel($factory);
renderSettingsPanel() could
accidentally end up with a LightButton next to a DarkCheckbox — the factory
you pass in guarantees every product it hands out belongs to the same family.
When to Reach for Abstract Factory Instead of Factory Method
Use plain Factory Method when you're choosing between variants of a single product. Reach for Abstract Factory once you need to guarantee that an entire set of objects are mutually compatible — cross-platform UI toolkits (Windows vs. macOS widgets), database layers that need a matching connection + query builder + schema-diff class per vendor, or export systems that need a matching writer + formatter + validator per file format.