Abstract Factory
Intent
Provide an interface for creating families of related objects without specifying their concrete classes. Where factory method creates one product, abstract factory creates a coordinated set — and its real product is the guarantee that pieces of a family always match.
The Problem Shape It Fits
A cross-platform UI toolkit renders buttons, checkboxes, inputs. Mixing a Windows button into a Mac dialog is a visual (and behavioral) bug — products must come from the same family. Ad-hoc creation (new WinButton(); new MacCheckbox();) has no way to enforce that. Abstract factory makes the family the unit of selection: pick WinFactory once, every product it emits is Windows-consistent.
«interface» GUIFactory «interface» Button «interface» Checkbox
+ createButton(): Button + paint() + paint()
+ createCheckbox(): Checkbox △ △
△ │ │
┌────────────┴───────────┐ ┌────┴────┐ ┌────┴─────┐
│ │ │WinButton│ │WinCheck │
┌────┴────────┐ ┌──────┴──────┐ └─────────┘ └──────────┘
│ WinFactory │ │ MacFactory │ △ △
│ +createBtn()│ │ +createBtn()│ ┌────┴─────┐ ┌────┴─────┐
│ +createChk()│ │ +createChk()│ │MacButton │ │MacCheck │
└─────────────┘ └─────────────┘ └──────────┘ └──────────┘
each factory returns ONE CONSISTENT FAMILY across all product types
Client code sees only interfaces; one injected factory decides the entire look.
Mechanics
interface GuiFactory {
Button createButton();
Checkbox createCheckbox();
}
class Application {
private final GuiFactory factory;
Application(GuiFactory f) { this.factory = f; }
void renderDialog() {
Button b = factory.createButton(); // family-consistent by construction
Checkbox c = factory.createCheckbox();
layout(b, c);
}
}
new Application(new MacFactory()).renderDialog(); // whole UI switches here
Structure Roles
| Role | Example |
|---|---|
| Abstract factory | GuiFactory |
| Concrete factories | WinFactory, MacFactory |
| Abstract products | Button, Checkbox |
| Concrete products | WinButton… MacCheckbox |
| Client | Application |
vs Factory Method
| Factory method | Abstract factory | |
|---|---|---|
| Products per decision | One | A family |
| Mechanism | One overridable method | Interface with multiple creation methods |
| New variant (Mac→Linux) | Add subclass | Add one factory class |
| New product type (add Slider) | Add to creator | Touch every factory — the known cost |
That last row is the trade-off in one line: variants are cheap, new product kinds are expensive.
Real-World Sightings
- JDBC:
Connectionis an abstract factory —createStatement()/prepareStatement()emit a consistent SQL-family toolkit for whatever database the driver serves. - Document exporters:
DocFactory.createHeader/createBody/createFooter()producing matched HTML or PDF parts. - Dependency-injection configurations choosing whole infrastructure families (test vs prod stores) at one line.
Interview Framing
- The consistency argument (“impossible to mix families”) is the intent interviewers listen for, not the UML.
- Naming the add-a-product-type weakness unprompted separates memorized diagrams from understood ones.
The Family Matrix
Every abstract factory is a matrix decision. Products (columns) × families (rows); each concrete factory fills one row consistently:
Button Checkbox Input ← product TYPES
┌─────────────┬──────────────┬─────────────┐
WinFactory │ WinButton │ WinCheckbox │ WinInput │ ← one VARIANT
├─────────────┼──────────────┼─────────────┤ per row,
MacFactory │ MacButton │ MacCheckbox │ MacInput │ complete
├─────────────┼──────────────┼─────────────┤
WebFactory │ HtmlButton │ HtmlCheckbox │ HtmlInput │
└─────────────┴──────────────┴─────────────┘
The two growth directions have opposite costs — the entire maintenance story of this pattern:
| Change | Cost | Why |
|---|---|---|
| New variant row (Linux family) | Cheap: write one new factory class | Nothing existing changes |
New type column (Slider) | Expensive: edit factory interface + every concrete factory | Interface is the contract; columns are baked in |
Before choosing the pattern, ask which direction the requirements actually grow. UI themes grow rows (good fit). Form builders that keep adding field kinds grow columns (bad fit — consider registries of independent factories per type instead).
Worked Family: Database Access
interface DbFactory {
Connection createConnection();
Statement createStatement();
Dialect dialect();
}
class MySqlFactory implements DbFactory {
public Connection createConnection() { return new MySqlConnection(cfg); }
public Statement createStatement() { return new MySqlStatement(); }
public Dialect dialect() { return Dialect.MYSQL; }
}
// PostgresFactory, TestInMemoryFactory ... same shape
class UserRepository {
private final DbFactory db;
UserRepository(DbFactory db) { this.db = db; }
User find(Id id) {
try (var st = db.createStatement()) { /* SQL via db.dialect() */ }
}
}
TestInMemoryFactory swapping production for an in-memory family in tests is the payoff line — whole infrastructure family switched by one constructor argument.
Consistency Enforcement Beyond Types
Type systems guarantee a factory returns its declared products — but not semantic consistency (a MySQL statement against a Postgres connection). Practical guardrails:
- Factories construct their own connections internally (never mix externally sourced parts).
- Keep cross-product assumptions inside the family’s classes.
- Integration-test each family as a unit.
Production Notes
- One factory instance per family per process; inject it (singleton scope typical).
- Selection at composition root only —
if (env.isProd()) new MySqlFactory() else new InMemoryFactory()belongs inmain/config, nowhere else. - Adding variant rows is so mechanical that reflection/registration-based discovery (classpath scanning) is common at scale — Spring’s auto-configuration is this idea industrialized.
Interview Framing
- Drawing the matrix before classes shows structured thinking; stating which axis grows decides whether abstract factory is even right.
Premium Content
Unlock Abstract Factory and all premium lessons with a subscription.
From ₹199.99/year — See plans