If you remember five things
The whole pattern in one glance. Everything below defends these five calls.
- Intent. Produce families of matching objects (a button plus a checkbox plus a scrollbar that all belong together) behind one interface, so the client never names a concrete class.
- The family constraint. Use it only when mixing families is a bug: a MacButton next to a WindowsCheckbox should be impossible, and the factory makes it impossible.
- Structure. One abstract factory interface, one concrete factory per family, abstract products, concrete products, and a client that touches only interfaces.
- Single decision point. The one place that picks a family is the composition root. Everything downstream stays blind to which family it got.
- The classic gotcha. It is Open/Closed along the family axis and hostile along the product-kind axis: a new product kind forces a new method on every concrete factory.
1The problem it solves#
One scenario shows up over and over: you are building a UI toolkit and need buttons, checkboxes, and scrollbars. Then the ask lands: "support macOS and Windows, make it look native on both."
The combinatorics get ugly fast.
- Two platforms times three widget kinds is already six concrete classes (MacButton, WindowsButton, MacCheckbox, and so on).
- A third platform pushes it to nine.
- Each one is a class you now have to create from somewhere.
The naive version branches everywhere. At every spot you build a widget you write:
if (platform == MAC) {
button = new MacButton();
} else {
button = new WindowsButton();
}
- You write that same
iffor the checkbox. And the scrollbar. And the menu. - Three widget kinds and two platforms means three copies of the same branch, each a place a future bug can hide.
- The branching spreads through the whole codebase like mold.
There are two real problems underneath, and the second is the sneaky one.
- Open/Closed violation. Business code knows every concrete class, so a third platform means editing the branch in every file. You cannot extend the system without reopening existing code.
- No pairing guarantee. Nothing stops you from pairing a MacButton with a WindowsCheckbox. The families are supposed to stay together, but the type system lets you mix them freely. The compiler will never catch that bug.
It fixes both at once. One object decides the whole family, and your code just asks it for parts. The client stops knowing concrete classes (Open/Closed is restored), and a single factory has no code path that can produce a mismatched part (the pairing bug becomes structurally impossible, not merely discouraged).
2The idea (mental model)#
Think of a kit. When you buy a furniture kit you get screws, bolts, and panels that all fit each other. You do not pick a screw individually and hope it matches the panel. The kit guarantees the fit.
- An abstract factory is that kit: an object whose entire job is to hand you a set of parts that belong together.
- You never say "give me a MacButton." You say "give me a button" to the Mac kit, and it decides.
- The one line to keep in your head: a factory of consistent sets. Choose the set once, at startup, and every part you pull after that is guaranteed to be from the same family.
Two principles do the heavy lifting.
- Dependency Inversion. The client depends on the
ButtonandGUIFactoryabstractions, never onMacButton. - Single Responsibility. The only thing that knows how to build a family is the factory for that family, so widget-building logic lives in one place per platform, not scattered across the app.
Abstract Factory
- Produces
- A whole matching set of products.
- Mechanism
- Composition: you hold a factory object.
- Shape
- Several create methods behind one interface.
- Grows well when
- You add new families.
Factory Method
- Produces
- One kind of product.
- Mechanism
- Inheritance: you override one method.
- Shape
- A single overridable creation hook.
- Grows well when
- You subclass to vary one product.
Interviewers love this exact distinction, so have the one-liner ready: Factory Method is one hook; Abstract Factory is a whole menu of hooks bundled behind one interface.
3Structure#
The moving parts, each with one job:
- Abstract Factory. An interface that declares one creation method per product kind:
createButton(),createCheckbox(). - Concrete Factories. One per family:
MacFactory,WindowsFactory. Each returns that family's products. - Abstract Products. The interfaces for each kind of thing:
Button,Checkbox. - Concrete Products. The actual classes:
MacButton,WindowsCheckbox. - Client. Your code. Holds a reference to the abstract factory and the abstract products, and never mentions a concrete class.
The key move: the client depends only on interfaces.
- The concrete factory is injected once, usually at the composition root.
- Everything downstream stays blind to which family it got.
- That single injection point is where the design earns its testability: pass a fake factory in a unit test and the client never knows the difference.
4Implementation in Java#
Start with the product interfaces. Keep them small.
interface Button {
void render();
}
interface Checkbox {
void render();
}
Now the concrete products, one set per platform.
class MacButton implements Button {
public void render() {
System.out.println("Rounded macOS button");
}
}
class MacCheckbox implements Checkbox {
public void render() {
System.out.println("macOS checkbox with a soft check");
}
}
class WindowsButton implements Button {
public void render() {
System.out.println("Square Windows button");
}
}
class WindowsCheckbox implements Checkbox {
public void render() {
System.out.println("Windows checkbox with a hard tick");
}
}
The factory interface. One method per product kind. This is the whole contract, and its size is the thing to watch: every method here must be implemented by every concrete factory, so this interface is the pattern's cost center.
interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
Two concrete factories. Each produces only its own family, so the pairing can never go wrong.
class MacFactory implements GUIFactory {
public Button createButton() {
return new MacButton();
}
public Checkbox createCheckbox() {
return new MacCheckbox();
}
}
class WindowsFactory implements GUIFactory {
public Button createButton() {
return new WindowsButton();
}
public Checkbox createCheckbox() {
return new WindowsCheckbox();
}
}
The client. Notice it never says new MacButton(). It takes a factory, builds its parts, and uses them, with no idea which platform it is on.
class Application {
private final Button button;
private final Checkbox checkbox;
Application(GUIFactory factory) {
this.button = factory.createButton();
this.checkbox = factory.createCheckbox();
}
void renderUI() {
button.render();
checkbox.render();
}
}
- That
GUIFactoryin the constructor is textbook constructor injection. - It is why you can test
Applicationagainst a stub factory with zero production classes involved.
The one place that picks the family. This is the composition root, the single spot allowed to know about concrete factories.
public class Main {
public static void main(String[] args) {
String os = System.getProperty("os.name").toLowerCase();
GUIFactory factory = os.contains("mac")
? new MacFactory()
: new WindowsFactory();
Application app = new Application(factory);
app.renderUI();
}
}
if copied once per widget kind. Where did all those branches go?Reveal the answer
if still exists, but it lives in exactly one line in one file, and it chooses an entire family at once instead of one widget. Three branches became one, and that one moved to the edge of the system where it belongs. Everywhere else is now clean. That collapse is the payoff you point at in the interview.Here is the runtime flow for one call, so you can see how the request travels:
GUIFactory and Button interfaces. The concrete MacFactory was chosen in Main and handed in. Application asks for a button and gets one back without ever learning its concrete type.5A real-world example#
The classic is cross-platform UI, and it is classic because it is true.
- Java's own Swing look-and-feel system works on this idea: a LookAndFeel object produces a matching set of UI delegates so your buttons, sliders, and menus all share one visual language.
- Switch the LookAndFeel and the whole set changes together. That is the pattern in production in the standard library.
It works well outside UI too. Cloud providers are a clean fit.
- One project needed a storage client, a queue client, and a secrets client, all from the same provider or nothing lined up.
- The build: a
CloudFactorywithcreateStorage(),createQueue(), andcreateSecrets(). AwsFactoryreturned S3, SQS, and Secrets Manager wrappers.GcpFactoryreturned the Google equivalents.- The app got one factory at boot, from config, and never cared again. Swapping providers in a test became a one-line change: pass the other factory, replacing what would have been three coordinated client swaps.
Database access layers that support more than one engine are another good fit.
- A
PostgresFactoryand aMySqlFactoryeach hand back a connection, a query builder, and a dialect object that all speak the same SQL flavor. - You never accidentally feed MySQL syntax to a Postgres connection, because the factory will not give you that mismatched pair.
The common thread in all three: a set of parts that must stay consistent, and a single decision point that picks the set.
6When to use it, and when not to#
Use it when:
- You have two or more product kinds that come in matching families, and mixing families across the line is a bug.
- You want to swap the whole set at once, at runtime or by config, without touching client code.
- The concrete classes are a detail your business logic should not know.
Skip it when:
- You only have one product kind. Then you want Factory Method or a plain factory. Abstract Factory earns its keep only when the family has multiple members.
- The families never actually vary. One platform that will only ever be one platform makes this ceremony with no payoff: a kit factory that produces a single kit.
- Adding a new product kind is more likely than adding a new family. This is the pattern's real weakness, covered next.
Every new product kind means a new method on the factory interface and a new method in every concrete factory. Two factories means three edits for one new widget kind; five factories means six edits. If you add product kinds often, the interface churns constantly. Abstract Factory is happy when families grow and hostile when the product list grows. If you cannot say this out loud unprompted, the interviewer reads you as someone who memorized the shape without understanding the cost.
7Common mistakes#
Confusing it with Factory Method.
- The single most common slip. Factory Method overrides one creation step through inheritance.
- Abstract Factory bundles several creation methods behind one interface, held by composition, to produce a whole family.
- If someone's "abstract factory" has one create method, it is really a factory method.
Letting concrete classes leak into the client.
- If
Applicationever writesnew MacButton()or casts aButtondown toMacButton, the pattern is broken. - One sneaky
instanceofcheck and you have undone it, because now the client knows the family and you are back to the branching you started with.
Overbuilding too early.
- People reach for this when there is one family and a vague "maybe someday" second one, ending up with four interfaces and six classes to produce one button.
- Build the simple version first. Introduce the factory when the second family actually arrives and the pattern pays for itself.
Forgetting the families must stay matched.
- If your
MacFactorycan somehow return aWindowsCheckbox, you have thrown away the guarantee that justifies the pattern. - Each concrete factory must return only its own family, always. The guarantee is only as strong as the weakest factory.
Scattering the family choice.
- The
if os == maccheck should exist in exactly one place. - Select the factory in three different files and you have recreated the original mess one level up, just with fancier types.
"What's the difference between Factory Method and Abstract Factory?"
"How would you add a new platform?"
"What's the downside?"
"Design a notification system for email, SMS, and push across multiple providers. Does this fit?"
"Someone calls a class with a single create method an Abstract Factory. Are they right?"
What you can now explain in the room
- The exact two problems it solves: the Open/Closed violation and the impossible-to-catch family-mixing bug.
- The structure (abstract factory, concrete factories, abstract and concrete products, client) and how to draw it in under two minutes.
- Why the client depends only on interfaces, and how that single injection point makes testing trivial.
- The one-liner that distinguishes it from Factory Method: a family through composition versus one product through inheritance.
- How adding a family is cheap (one class, zero edits) while adding a product kind is expensive (N+1 edits).
- When to skip it entirely: one product kind, families that never vary, or product kinds that churn more than families.
Next, compare this with the Factory Method and Builder patterns, and try applying it to a notification-service or payment-gateway design problem.
Get a prep plan built around your Abstract Factory Pattern interview
Tell us the role and your interview date. We will send back a dated, day by day plan. Free.