Provide an interface for creating families of related objects without specifying their concrete classes — ensuring products from the same family are always used together.
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.
These two patterns are often confused. Here's the key difference:
| Aspect | Abstract Factory | Factory Method |
|---|---|---|
| Creates | A family of related objects | One type of object |
| Interface | Multiple creation methods | One creation method |
| Consistency | Products guaranteed compatible | No such guarantee |
| Mechanism | Composition — inject the factory | Inheritance — subclass overrides |
| Use when | Multiple related products vary together | One 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.
Declares creation methods for each distinct product type in the family (e.g., createButton(), createCheckbox()). All concrete factories implement this interface.
Implements the abstract factory interface for a specific product family (e.g., WindowsUIFactory, MacUIFactory). Creates only products from its own family.
Interface for each distinct type of product (Button, Checkbox, TextInput). Concrete products implement these interfaces.
The real objects created by a concrete factory (WindowsButton, MacButton). Products from the same factory are guaranteed to be compatible with each other.
Uses only the abstract factory and abstract product interfaces. Never refers to concrete classes — the factory produces all objects, ensuring consistency.
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.
// 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
// 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 your app must run on multiple platforms (Windows, macOS, Web) and each platform needs its own look-and-feel for every component.
WindowsUIFactory, MacUIFactory, WebUIFactoryWhen switching environments (local, staging, production) should swap the entire infrastructure family — database, cache, and message queue together.
LocalDevFactory, AWSFactory, GCPFactoryWhen the entire UI must switch coherently between themes (light/dark, brand A/brand B) and partial swaps are unacceptable.
LightThemeFactory, DarkThemeFactoryWhen query builders, connection objects, and result parsers must all match the same database vendor.
MySQLFactory, PostgresFactory, SQLiteFactoryJava'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'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'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'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.