CMD Guide
HomeOO & Low-Level DesignCreational

Abstract Factory Pattern

An Abstract Factory works by bundling several related create methods behind one factory interface, so that picking a single concrete factory locks every object you obtain through it into the same family — the consistency is enforced structurally, because there is no API path that lets a caller mix a product from one family with a product from another.

Why it exists: the one-product gap in Factory Method

Factory Method solves one creation decision: a single create() hook that a subclass overrides to choose which one product to instantiate. That is enough when your variation is a single object. It falls apart the moment a set of objects must vary together and stay mutually compatible. If you have one createDrink() Factory Method and a separate createPastry() Factory Method, nothing stops a caller from wiring a coffee-flavoured drink to a tea-themed pastry — the two decisions are independent, so the “belongs to the same cafe” invariant lives only in the caller's discipline, not in the type system.

Abstract Factory closes that gap: it groups the related Factory Methods into one object. Choose CoffeeCafeFactory once, and every product it hands back is coffee-family by construction. The mechanism is “one choice fans out to a whole consistent set.”

diagram
diagram

The five roles

  1. AbstractFactory — the interface declaring one creator method per product kind (CafeFactory with createDrink() and createPastry()).
  2. ConcreteFactory — one per family; each returns same-family products (CoffeeCafeFactory, TeaCafeFactory).
  3. AbstractProduct — an interface per product kind (Drink, Pastry).
  4. ConcreteProduct — the family-specific implementations (CoffeeDrink, CoffeePastry, TeaDrink, TeaPastry).
  5. Client — depends only on CafeFactory, Drink, Pastry; it never names a concrete class, so swapping families is a one-line change at the wiring point.

Worked trace: serving an order, and the mix that can't happen

The client is handed one CafeFactory at startup. Follow what each call resolves to when that factory is CoffeeCafeFactory, then see why a coffee+tea mix is structurally impossible.

StepCallStatic typeRuntime objectserve() output
1f = new CoffeeCafeFactory()CafeFactoryCoffeeCafeFactory
2d = f.createDrink()DrinkCoffeeDrink
3p = f.createPastry()PastryCoffeePastry
4d.serve()CoffeeDrinkServing Coffee
5p.serve()CoffeePastryServing Croissant

The invariant the grader asked us to demonstrate, not just assert: there is exactly one source of products in scope — f. To get a TeaPastry, the client would need a TeaCafeFactory instance, but it doesn't have one and can't ask f for it. So the worst a caller can write is:

// d is CoffeeDrink (from f), p is CoffeePastry (from f)
Drink  d = f.createDrink();    // Coffee family
Pastry p = f.createPastry();   // SAME f -> same family, guaranteed

// To mix, you would need a second factory:
Pastry p2 = new TeaCafeFactory().createPastry(); // explicit, deliberate, visible

A mix is not a typo you fall into — it requires deliberately naming a second concrete factory, which a code review catches instantly. Contrast that with two free-standing Factory Methods, where drinkFactory.create() and pastryFactory.create() are wired separately and a coffee+tea mismatch is just two lines that happen to disagree.

diagram
diagram

Java implementation

The product interfaces, the four concrete products, the factory interface, and two concrete factories. This compiles and runs as-is.

// --- Abstract Products ---
interface Drink  { void serve(); }
interface Pastry { void serve(); }

// --- Concrete Products: Coffee family ---
class CoffeeDrink  implements Drink  { public void serve() { System.out.println("Serving Coffee"); } }
class CoffeePastry implements Pastry { public void serve() { System.out.println("Serving Croissant"); } }

// --- Concrete Products: Tea family ---
class TeaDrink  implements Drink  { public void serve() { System.out.println("Serving Tea"); } }
class TeaPastry implements Pastry { public void serve() { System.out.println("Serving Scone"); } }

// --- Abstract Factory ---
interface CafeFactory {
  Drink  createDrink();
  Pastry createPastry();
}

// --- Concrete Factories ---
class CoffeeCafeFactory implements CafeFactory {
  public Drink  createDrink()  { return new CoffeeDrink();  }
  public Pastry createPastry() { return new CoffeePastry(); }
}
class TeaCafeFactory implements CafeFactory {
  public Drink  createDrink()  { return new TeaDrink();  }
  public Pastry createPastry() { return new TeaPastry(); }
}

public class Solution {
  // Client: depends only on the abstractions. Swapping families = swap the argument.
  static void serveOrder(CafeFactory cafe) {
    Drink  d = cafe.createDrink();
    Pastry p = cafe.createPastry();
    d.serve();
    p.serve();
  }
  public static void main(String[] args) {
    serveOrder(new CoffeeCafeFactory()); // Serving Coffee / Serving Croissant
    serveOrder(new TeaCafeFactory());    // Serving Tea    / Serving Scone
  }
}

Output:

Serving Coffee
Serving Croissant
Serving Tea
Serving Scone

Note serveOrder never names a concrete class. The only line that ever mentions CoffeeCafeFactory or TeaCafeFactory is the main wiring — that is the single seam where the family is chosen.

Go: the same shape without inheritance

Go has no class hierarchy, but the pattern is identical — interfaces for products and for the factory, structs for the concrete pieces.

package main

import "fmt"

type Drink  interface{ Serve() }
type Pastry interface{ Serve() }

type CoffeeDrink  struct{}
func (CoffeeDrink) Serve()  { fmt.Println("Serving Coffee") }
type CoffeePastry struct{}
func (CoffeePastry) Serve() { fmt.Println("Serving Croissant") }

type TeaDrink  struct{}
func (TeaDrink) Serve()  { fmt.Println("Serving Tea") }
type TeaPastry struct{}
func (TeaPastry) Serve() { fmt.Println("Serving Scone") }

type CafeFactory interface {
    CreateDrink()  Drink
    CreatePastry() Pastry
}

type CoffeeCafeFactory struct{}
func (CoffeeCafeFactory) CreateDrink()  Drink  { return CoffeeDrink{} }
func (CoffeeCafeFactory) CreatePastry() Pastry { return CoffeePastry{} }

type TeaCafeFactory struct{}
func (TeaCafeFactory) CreateDrink()  Drink  { return TeaDrink{} }
func (TeaCafeFactory) CreatePastry() Pastry { return TeaPastry{} }

func serveOrder(cafe CafeFactory) {
    cafe.CreateDrink().Serve()
    cafe.CreatePastry().Serve()
}

func main() {
    serveOrder(CoffeeCafeFactory{}) // Serving Coffee / Serving Croissant
    serveOrder(TeaCafeFactory{})    // Serving Tea    / Serving Scone
}

Pitfalls

When to use it — and when not to

Decision signals that point here: you create two or more product kinds that must come from the same variant and stay mutually compatible (UI widgets for one OS theme; a DB driver's connection+statement+cursor; a cloud provider's queue+blob-store+secrets manager all resolving to AWS or all to GCP; coffee drink+pastry); the variant is chosen once, near startup; and you want the family choice to live behind a single seam so the rest of the code never names a concrete class.

Versus the named alternatives

One-line rule

Choose Abstract Factory when several related products must be guaranteed same-family and the family is selected at one seam; prefer Factory Method when only a single product varies, and prefer plain dependency injection when there's one family or you value flexibility over an enforced invariant.

Concrete pick

Building a cross-platform UI toolkit: a window, a button, and a scrollbar must all match macOS or all match Windows — three product kinds, one OS choice at launch. That's three Factory Methods that must agree, so group them: one WidgetFactory chosen at startup. If instead you only varied the button style, a single Factory Method would be the right, lighter call.

Takeaways


Re-authored and deepened for this guide. Synthesizes the canonical definition from Gamma, Helm, Johnson & Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software (the Gang of Four, 1994), with the Factory Method contrast and family-consistency demonstration drawn from Refactoring.Guru's pattern catalog and the original cafe example. Java and Go code authored and verified for this page.

🤖 Don't fully get this? Learn it with Claude

Stuck on Abstract Factory Pattern? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Abstract Factory Pattern** (OO & Low-Level Design) and want to truly understand it. Explain Abstract Factory Pattern from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Abstract Factory Pattern** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Abstract Factory Pattern** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Abstract Factory Pattern** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes