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.”
The five roles
- AbstractFactory — the interface declaring one creator method per product kind (
CafeFactorywithcreateDrink()andcreatePastry()). - ConcreteFactory — one per family; each returns same-family products (
CoffeeCafeFactory,TeaCafeFactory). - AbstractProduct — an interface per product kind (
Drink,Pastry). - ConcreteProduct — the family-specific implementations (
CoffeeDrink,CoffeePastry,TeaDrink,TeaPastry). - 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.
| Step | Call | Static type | Runtime object | serve() output |
|---|---|---|---|---|
| 1 | f = new CoffeeCafeFactory() | CafeFactory | CoffeeCafeFactory | — |
| 2 | d = f.createDrink() | Drink | CoffeeDrink | — |
| 3 | p = f.createPastry() | Pastry | CoffeePastry | — |
| 4 | d.serve() | — | CoffeeDrink | Serving Coffee |
| 5 | p.serve() | — | CoffeePastry | Serving 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.
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
- Adding a new product kind is a breaking change. Introduce a
createMug()and theCafeFactoryinterface grows a method — every concrete factory must now implement it or the code won't compile. Adding a new family (a JuiceBarFactory) is cheap; adding a new product axis is expensive. This asymmetry is the pattern's core trade-off, not a bug. - Leaking concrete types defeats it. If the client does
(CoffeeDrink) f.createDrink()or branches oninstanceof, you've reintroduced the coupling the pattern removed. Keep returns typed as the abstract product. - Over-engineering when there's only one family. If you will only ever have coffee, the abstract factory is pure ceremony — a plain constructor is correct. The pattern earns its keep only when families genuinely vary at runtime or deploy time.
- Confusing it with a Factory Method. A single overridden
create()returning one product is Factory Method, even if someone named the class...AbstractFactory. Abstract Factory is specifically the grouping of multiple related creators. - Hidden global state in the factory. Teams often make the concrete factory a singleton holding config. That couples object creation to global mutable state and makes tests order-dependent — prefer passing the factory in (dependency injection) over a static lookup.
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
- Factory Method — one creator, one product. Gain: far less ceremony, trivial to add product subtypes. Cost: no cross-product consistency guarantee — nothing stops a coffee+tea mix. Choose Factory Method when the variation is a single object; reach for Abstract Factory only when a set must vary together.
- Builder — constructs one complex object step by step. Gain: handles many optional parameters and assembly order. Cost: it builds one thing, not a family of interchangeable things. Use Builder for “configure this object”; use Abstract Factory for “pick which family of objects.”
- Plain DI / a config map — inject the concrete products directly. Gain: zero pattern overhead, maximum flexibility, easy to test. Cost: the “same family” invariant is now the wiring code's responsibility, not the type system's — fine for a small app, risky when many call sites create products.
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
- Abstract Factory = a bundle of related Factory Methods behind one interface, so one factory choice produces a whole consistent family — the mechanism, not just the label.
- It exists precisely to fix Factory Method's gap: independent creators let incompatible products mix; grouping them makes a cross-family mix require deliberately naming a second concrete factory.
- Cheap to add a new family, expensive to add a new product kind (every factory must change) — design the product axes up front.
- Skip it when there's one family; a Factory Method or plain DI is the honest, lighter choice.
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.
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.
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.
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.
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.