AcademyDesign Patterns
Abstract Factory pattern
Guaranteeing a whole family of related objects ships together, the extra interface every product carries, and the mismatch when a factory returns the wrong family.
7 min readUpdated 3 exercises
Abstract Factory is what Factory Method grows into once one product isn't enough, when what's needed is a whole matched family of related objects, guaranteed never to mix.
A Factory Method returns one interchangeable product. An Abstract Factory returns several, a whole family that has to agree with each other, guaranteed by construction. Pick a factory once, and every product it hands you afterward belongs to the same family. There is no code path where a caller ends up mixing a piece from one family with a piece from another, because nothing ever calls the two families' constructors side by side.
The problem it solves
Your app supports a light and a dark theme. The theme is not one object, it is a set: buttons, checkboxes, inputs, modals, each with its own light and dark version.
If each component is built by its own factory function, nothing links them. One screen builds a dark button because a flag was read at the top, another builds a light checkbox because it read the flag later, after the user toggled it. The result is a screen with a dark button sitting next to a light checkbox.
- The mismatch is not a crash. It is a visual bug that only appears in one combination and is easy to miss in review.
- Every place that builds a piece has to remember to ask for the same variant.
- Adding a third theme means finding all of those places again.
The fix is to choose the family once, in one object, and let that object build every piece. Callers never pick a variant, so they cannot pick inconsistently.
Structure
Four parts:
- Abstract products (
Button,Checkbox): the interfaces callers use. - Concrete products (
LightButton,DarkCheckbox, and so on): one per product per family. - An abstract factory interface (
UIFactory) with one creation method per product. - Concrete factories (
LightUIFactory,DarkUIFactory): each returns products from one family only.
class LightButton { render() { return 'light-button'; } }
class LightCheckbox { render() { return 'light-checkbox'; } }
class DarkButton { render() { return 'dark-button'; } }
class DarkCheckbox { render() { return 'dark-checkbox'; } }
class LightUIFactory {
createButton() { return new LightButton(); }
createCheckbox() { return new LightCheckbox(); }
}
class DarkUIFactory {
createButton() { return new DarkButton(); }
createCheckbox() { return new DarkCheckbox(); }
}
function renderForm(factory) {
return [factory.createButton().render(), factory.createCheckbox().render()];
}
// renderForm(new LightUIFactory()) → ['light-button', 'light-checkbox']
// renderForm(new DarkUIFactory()) → ['dark-button', 'dark-checkbox']Nothing inside renderForm decides which family it is rendering. It only calls the two methods every UIFactory promises to have, and whichever factory it was handed decides what those methods return together. That is the guarantee a single Factory Method cannot give you: with one factory function per product, nothing stops a caller from building a light button next to a dark checkbox by mistake.
Where it is used in real systems
| Use case | The family being kept consistent |
|---|---|
| Theming and design systems | Light or dark, or a per-brand skin, across every component. |
| Cross-platform UI | Buttons, dialogs and menus that match Windows, macOS or the web. |
| Database dialects | Connection, query builder and migration runner that all speak Postgres, or all speak MySQL. |
| Cloud providers | Storage, queue and email clients that all come from AWS, or all from Cloudflare. |
| Test environments | Repositories, mailer and clock that are all real, or all in-memory fakes. |
| Localization | Date, number and currency formatters that all follow the same locale. |
The test environment row is the one worth remembering. Wiring a whole set of fakes together at startup, instead of mocking each dependency in each test, is Abstract Factory in daily use. The rule in every row is the same: the pieces only make sense together.
Abstract Factories you already use
- Design system providers. A theme provider that hands every child component its colors and spacing is choosing a family once, at the top.
- Database drivers. A Knex or TypeORM dialect gives you a connection, a query compiler and a schema builder that all match the same database.
- Dependency injection modules. A NestJS or Angular module that registers "the production set" or "the test set" of providers is a factory for a family.
- Cross-platform toolkits. Any library with a per-OS backend for windows, dialogs and file pickers.
The cost
Abstract Factory trades one kind of change for another.
- Adding a new family is easy. Write one new factory and its products. No caller changes.
- Adding a new product type is hard. Add a
createSlider()and every existing factory has to implement it, or the code stops compiling.
So it fits when the families change often and the set of product types is stable. If it is the other way around, the pattern fights you.
When not to use it
- Only one family exists. With a single theme,
UIFactoryis an interface with one implementation and no reason to exist. - The products do not need to match. If a light button next to a dark checkbox is harmless, plain functions or Factory Method are enough.
- Two products, no growth. For one or two objects, a config value and a simple function are lighter.
What interviewers ask
- "Abstract Factory versus Factory Method?" One product versus a family that must stay consistent.
- "What problem does it solve?" Preventing mismatched products by making the family a single choice.
- "What is its main drawback?" Adding a product type touches every factory.
- "Where would you use it in a real backend?" Swapping a whole set of production dependencies for in-memory ones in tests.
- "How does it relate to dependency injection?" A DI container often plays the role of the abstract factory, deciding which family of implementations to wire.
The mistake to avoid
An Abstract Factory whose product methods can return a type from the wrong family. Nothing catches that at runtime in a dynamically typed language, the two products just silently disagree the first time they are used together. Type the factory's methods with the abstract interfaces and keep each concrete factory returning only its own family.
Abstract Factory is one naming device among several an object-oriented language gives you for controlling how
newgets called. The mechanism above is the same, syntax aside, whether the language is JavaScript, Python or Java.
Now practice it
1 challenge · 2 quizzesReading this page is the cheap half. These are the exercises that make you use it.
- Abstract FactoryProBuild a factory that returns a matched family of products and prove nothing lets them mix — the guarantee this page diagrams, made runnable.ChallengeSolve · MediumChallengeSolve · Medium
- Design Patterns · Creational PatternsTen quick questions across Singleton, Factory Method, Abstract Factory, Builder and Prototype, to check the mental model before this one.QuizTake · EasyQuizTake · Easy
- Design Patterns · Creational Patterns — Series 2ProA second, tougher pass on the same creational family, for once the first set feels easy.QuizTake · EasyQuizTake · Easy
Read next
Factory Method pattern
Hiding which concrete class gets built behind one function, the indirection it costs when the choice might never grow, and the leak when a return type still names the class.
7 min
Prototype pattern
Cloning a configured instance instead of rebuilding from scratch, the per-field copy it costs, nested fields included, and a shallow clone() copying a reference, not a value.
6 min