Abstract Factory Pattern
Learn how to guarantee that a whole family of related objects is created consistently together, using a cross-platform UI kit where a button and a scroll bar must always match the same theme.
Learning objectives
- Identify when multiple related products must stay consistent as a matched family, not just individually correct
- Build one factory interface with a creation method per product in the family
- Explain why Abstract Factory makes a mismatched combination structurally impossible, not just discouraged
- Compare Abstract Factory against a plain Factory Method and know when each is the right tool
◆ Story
A furniture showroom sells matched sets — a "Modern" collection's sofa, chair, and coffee table are all designed to look right together, and a "Rustic" collection's pieces are all designed to look right together. Nobody wants a customer accidentally buying a Modern sofa with a Rustic coffee table — the whole point of a collection is that every piece within it is guaranteed compatible with every other piece in that same collection.
That guarantee — an entire matched set staying matched — is what the Abstract Factory pattern protects, one level above what a plain Factory can guarantee for a single product.
A cross-platform application needs the same guarantee. It renders a button and a scroll bar, styled consistently for whichever platform it runs on — Windows or Mac — with the ability to switch themes entirely without touching the code that uses these components. The obvious first version constructs each platform-specific component directly, wherever it is needed. It is worth building that version first, and seeing exactly what stops a Windows button from accidentally ending up next to a Mac scroll bar.
The obvious way to render a themed UI is to define one concrete class per platform per component — WindowsButton, WindowsScrollBar, MacButton, MacScrollBar — and construct whichever pair the application needs, directly, in its startup code.
This renders a consistent Windows-themed UI just fine. The question worth asking is: what actually stops a future edit from picking a WindowsButton and a MacScrollBar at the same time? The honest answer is nothing. Nothing in this code enforces that related components stay together — new WindowsButton() paired with new MacScrollBar() compiles perfectly fine and produces a logically inconsistent, visually broken UI.
The client is also tightly coupled to every concrete class for every platform it supports; switching themes means editing this code directly, and adding Linux support means going back in and adding a third set of explicit construction calls, editing already-working code. A plain Factory Method would fix the coupling problem for one product at a time — a button factory here, a separate scroll-bar factory there — but two independent factories still would not stop someone from picking a Windows result out of one and a Mac result out of the other. The actual missing guarantee is that an entire group of related products comes from the same, consistent source.
💻 Code example
class WindowsButton { void render() { System.out.println("Rendering Windows button"); } } class WindowsScrollBar { void scroll() { System.out.println("Scrolling Windows scroll bar"); } } class MacButton { void render() { System.out.println("Rendering Mac button"); } } class MacScrollBar { void scroll() { System.out.println("Scrolling Mac scroll bar"); } } class Application { public static void main(String[] args) { WindowsButton button = new WindowsButton(); WindowsScrollBar scrollBar = new WindowsScrollBar(); button.render(); scrollBar.scroll(); } } // Renders a consistent Windows UI. But nothing stops a future edit // from picking a WindowsButton and a MacScrollBar at the same time.
A button and a scroll bar rendered in the same UI need to come from the same theme — that is a relationship between two products, not just a property of one. So instead of one factory per product, there needs to be one factory per theme: a single object responsible for creating every component a given theme needs, together. Every theme's factory should also expose the exact same set of creation methods, so the client can work with any theme's factory identically, without knowing which one it is holding.
That is the Abstract Factory pattern: define one interface with a creation method for every product in the family, and let each concrete factory implementation guarantee it produces a consistent, matching set.
First, Button and ScrollBar become shared interfaces, and WindowsButton, WindowsScrollBar, MacButton, and MacScrollBar implement them. Then UIFactory declares one method per product — createButton() and createScrollBar() — and WindowsFactory and MacFactory each implement both methods, but only ever reference their own platform's concrete classes internally. It is structurally impossible for WindowsFactory to ever hand back a MacScrollBar, because that class does not even appear anywhere inside WindowsFactory's own source. The guarantee is not a convention anyone has to remember — it is built into which concrete factory class you happen to be holding.
💻 Code example
interface Button { void render(); } interface ScrollBar { void scroll(); } class WindowsButton implements Button { public void render() { System.out.println("Rendering Windows button"); } } class WindowsScrollBar implements ScrollBar { public void scroll() { System.out.println("Scrolling Windows scroll bar"); } } class MacButton implements Button { public void render() { System.out.println("Rendering Mac button"); } } class MacScrollBar implements ScrollBar { public void scroll() { System.out.println("Scrolling Mac scroll bar"); } } interface UIFactory { Button createButton(); // every theme's factory must be able to make BOTH of these ScrollBar createScrollBar(); } class WindowsFactory implements UIFactory { public Button createButton() { return new WindowsButton(); } public ScrollBar createScrollBar() { return new WindowsScrollBar(); } // GUARANTEED to match } class MacFactory implements UIFactory { public Button createButton() { return new MacButton(); } public ScrollBar createScrollBar() { return new MacScrollBar(); } }
Application is rebuilt to receive a UIFactory — not any specific component — and asks that one factory for both the button and the scroll bar it needs. windowsApp.renderUI() prints "Rendering Windows button" and "Scrolling Windows scroll bar," exactly as before.
Switching the entire theme now takes one line: construct a MacFactory instead of a WindowsFactory, and pass it to the same Application constructor. Application's own code does not change at all — not one line — and the result is "Rendering Mac button" and "Scrolling Mac scroll bar." Compare this to the naive version: there, mixing themes was a mistake a developer could accidentally make. Here, it is not a mistake anyone has to avoid, because there is no code path that could even construct a mismatched pair — Application only ever asks one factory for both of its components.
💻 Code example
class Application { private final Button button; private final ScrollBar scrollBar; Application(UIFactory factory) { // receives WHICH theme, not which specific components this.button = factory.createButton(); this.scrollBar = factory.createScrollBar(); } void renderUI() { button.render(); scrollBar.scroll(); } } public class Main { public static void main(String[] args) { UIFactory windowsFactory = new WindowsFactory(); Application windowsApp = new Application(windowsFactory); windowsApp.renderUI(); // "Rendering Windows button" / "Scrolling Windows scroll bar" // Switching themes entirely -- one line changes: UIFactory macFactory = new MacFactory(); Application macApp = new Application(macFactory); macApp.renderUI(); // "Rendering Mac button" / "Scrolling Mac scroll bar" // Application's own code did not change at all. } }
▲ Edge case: do not reach for this when there is only ever one product
If Application only ever needed a button, and never a scroll bar, Abstract Factory would be unnecessary machinery — a plain Factory Method is the right, sufficient tool whenever there is no must-stay-consistent family relationship to protect.
▲ Edge case: adding a new product to the family means touching every existing factory
Adding a Checkbox to the family means adding createCheckbox() to the UIFactory interface — and now every existing concrete factory, WindowsFactory, MacFactory, and any others, must implement it too, or the code will not compile. This is the real, honest cost of Abstract Factory's guarantee: adding a new product to the family is not free, the way adding a new theme is. Adding a whole new theme instead, such as a LinuxFactory, costs nothing to any existing factory — it is one new class implementing the existing interface, with zero changes anywhere else. Abstract Factory is cheap to extend with new families and comparatively expensive to extend with new products.
▲ Common mistake: confusing family consistency with simple type selection
It is easy to reach for Abstract Factory whenever more than one product type exists, but the pattern's actual value only shows up when those products must stay consistent as a set. If a button and a scroll bar never actually needed to match, a plain Factory for each would be simpler and equally correct.
◆ Under the hood
Abstract Factory shows up wherever a set of related objects must be created together, guaranteed to work as a matched set.
- Cross-platform GUI toolkits — this chapter's exact scenario, at real scale: a full widget set styled consistently for whichever operating system the application is running on.
- JDBC driver families — a JDBC driver for one specific database vendor provides a matched connection, statement, and result-set implementation, all guaranteed to work together, rather than a caller mixing pieces from different vendors' drivers.
- Cloud SDK abstractions — a set of matched compute, storage, and networking clients for one specific cloud provider, all obtained from the same provider-specific factory or client builder.
In every case, picking one factory guarantees that every related object it hands back is designed to work together, without the caller having to verify compatibility itself.
Q: What does Abstract Factory guarantee that several separate, independent Factories cannot? : That every product created from one factory instance belongs to the same consistent family — impossible to accidentally mix a Windows button with a Mac scroll bar.
Q: When should you reach for Abstract Factory instead of a simple Factory? : When you have multiple related products that must stay consistent as a family — not when you are only ever creating one kind of product.
Q: Why is it structurally impossible for WindowsFactory to return a MacScrollBar? : Because MacScrollBar is never referenced anywhere inside WindowsFactory's own source code — the guarantee comes from which concrete factory class you are holding, not from a runtime check.
Q: What is the real cost of adding a new product, like a Checkbox, to an existing Abstract Factory family? : Every existing concrete factory implementing the interface must add an implementation for the new creation method, or the code will not compile — unlike adding a new theme, which requires no changes to existing factories at all.
Q: How does Application avoid ever holding a mismatched pair of components? : It receives a single UIFactory and asks that same instance for both the button and the scroll bar — it never independently constructs or requests components from two different sources.
Want a visual for this concept?
Generate a diagram tailored to “Abstract Factory Pattern” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.
Sign in to generate a visual →