Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

Abstract Factory
LLD

Abstract Factory

Learn how to create families of related objects without coupling code to concrete implementations.

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

RoleExample
Abstract factoryGuiFactory
Concrete factoriesWinFactory, MacFactory
Abstract productsButton, Checkbox
Concrete productsWinButtonMacCheckbox
ClientApplication

vs Factory Method

Factory methodAbstract factory
Products per decisionOneA family
MechanismOne overridable methodInterface with multiple creation methods
New variant (Mac→Linux)Add subclassAdd one factory class
New product type (add Slider)Add to creatorTouch 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: Connection is 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:

ChangeCostWhy
New variant row (Linux family)Cheap: write one new factory classNothing existing changes
New type column (Slider)Expensive: edit factory interface + every concrete factoryInterface 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 in main/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.

My Private Notes

Notes are auto-saved locally to this device.