What problem does the Abstract Factory design pattern solve, and what does it mean that it creates a "family" of objects?
answer
- one method per product kind
- variants × kinds grid
- consistency enforced by construction
- concrete names only at composition root
- new kind ⇒ touch every factory
basics
~20 sAbstract Factory gives you one interface with several creation methods that together produce a set of related objects. Client code asks the interface for parts and never names concrete classes, so swapping one factory swaps the whole matching set.
solid answer
~50 sAbstract Factory declares an interface with one creation operation per product kind (e.g. createButton, createCheckbox, or createConnection/createStatementBuilder/createDialect). Each concrete factory implements all of them with one coherent variant — a "family". Client code is written against the abstract factory and the abstract product interfaces only, so it never mentions a concrete class name; the choice of family happens once, at composition time (config, startup wiring, dependency injection). Two benefits follow. First, substitutability: switching from the Dark family to the Light family, or Postgres to Oracle, is a single wiring change. Second, and more specific to this pattern, consistency: because one factory produces all the parts, the type system makes it hard to accidentally combine a Dark button with a Light checkbox. The cost is indirection and a rigid factory interface: adding a new product kind touches every factory implementation.
code
typescript · 21 linesinterface Button { render(): void }
interface Checkbox { render(): void }
interface GuiFactory {
createButton(): Button
createCheckbox(): Checkbox
}
class DarkFactory implements GuiFactory {
createButton() { return new DarkButton() }
createCheckbox() { return new DarkCheckbox() }
}
// Client depends on interfaces only.
function buildScreen(f: GuiFactory) {
const parts = [f.createButton(), f.createCheckbox()]
parts.forEach(p => p.render())
}
// Composition root — the only place a concrete class is named.
buildScreen(config.theme === 'dark' ? new DarkFactory() : new LightFactory())go deeper
State the intent: one interface with several creation methods, clients use interfaces only, swap the factory to swap the whole set. Give a UI-theme or database-driver example.
Add the consistency guarantee (a single factory instance makes mixed-variant bugs unrepresentable) and name the composition root as the one place concrete classes appear.
Discuss the variants × kinds grid, the asymmetric extensibility (cheap to add a variant, expensive to add a kind), and when a DI container or plain configuration is the simpler answer.
Frame it as a decision about which axis of change you are buying flexibility on, note that the abstract factory interface becomes a published contract that is costly to evolve, and describe migration paths (registry, capability lookup) when the family stops being uniform.
### The vocabulary first - **Concrete class**: the actual implementation type you would name when constructing an object directly (`new DarkButton()`). - **Abstract product**: an interface or abstract type describing what a product *does* (`Button` with `render()` and `onClick()`), independent of how it is implemented. - **Concrete product**: an implementation of an abstract product belonging to one variant (`DarkButton`, `LightButton`). - **Product family**: a set of *different* product kinds that belong together and are meant to be used as a group — e.g. `{Button, Checkbox, ScrollBar}` all in the Dark variant, or `{Connection, QueryBuilder, SqlDialect}` all for Postgres. - **Abstract factory**: an interface declaring **one creation method per product kind** in the family. - **Concrete factory**: one implementation of that interface per variant, returning that variant's concrete products — but *declaring* the abstract product types as return types. ### The problem it addresses Without the pattern, code that needs several related objects hardcodes their concrete classes: ``` button = new DarkButton() checkbox = new DarkCheckbox() scrollbar= new LightScrollBar() // oops — wrong variant, compiles fine ``` Two separate pains appear: 1. **Coupling to concrete classes.** Every construction site must be edited to support a new variant, and the client can only be tested against real implementations. 2. **Family consistency.** Nothing prevents mixing variants. The bug above is silent: it type-checks, it runs, it just looks wrong (or, in a persistence example, produces SQL the connected database cannot parse). Abstract Factory collapses both problems into a single decision point: ``` interface GuiFactory { Button createButton() Checkbox createCheckbox() ScrollBar createScrollBar() } class DarkFactory implements GuiFactory { ... returns Dark* ... } class LightFactory implements GuiFactory { ... returns Light* ... } // client — knows GuiFactory, Button, Checkbox, ScrollBar and nothing else class Screen { Screen(GuiFactory f) { this.button = f.createButton(); this.check = f.createCheckbox(); } } ``` The concrete class names now appear in exactly one place: the **composition root** — the startup code that decides which factory to instantiate, typically from configuration, an environment variable, an OS check, or a dependency-injection container binding. ### Why "family" is the load-bearing word The pattern is not "a factory that is abstract". Its distinguishing property is the **cross-product structure**: variants × product kinds. Because a single object supplies every kind for one variant, the invariant *"all parts come from the same variant"* is enforced structurally rather than by discipline. That is the guarantee you cite when an interviewer asks why not just use several independent factories. ### The mechanism, step by step 1. Identify the product kinds that must vary **together**. 2. Extract an abstract product interface for each kind. 3. Declare the abstract factory with one creation method per kind. 4. Implement one concrete factory per variant. 5. Write clients against abstract types only. 6. Choose and inject the concrete factory once, at startup. ### Costs and edge cases - **Indirection tax.** Two extra type hierarchies (factories and products) for what used to be a constructor call. Not worth it for one variant, or when variants will never grow past one. - **Rigid interface.** Adding a *new product kind* (say `createSlider()`) forces a change in the abstract factory and in **every** concrete factory. The pattern is open for extension along the *variant* axis and closed-ish along the *product-kind* axis. This asymmetry is the classic senior follow-up. - **Parameterless creation.** Creation methods usually take no arguments so that clients stay ignorant of construction details; when products genuinely need runtime arguments, they must be threaded through the factory signature (and then every variant must accept them), which is where the design starts to strain. - **Cross-family objects.** Sometimes a product needs a sibling from the same family (a `DarkButton` needing a `DarkTheme`). Concrete factories can pass themselves or shared state to the products they build. - **Not a singleton requirement.** Concrete factories are often stateless and shared, but the pattern says nothing about lifecycle; that is a separate decision. ### Where you have already seen it JDBC-style database drivers (a driver yields connections, statements and metadata that all belong to one vendor), XML/DOM `DocumentBuilderFactory`, GUI look-and-feel toolkits, and cloud-SDK client builders that hand out a matched set of service clients for one provider or region.
- If the only goal were removing `new` from the client, would Abstract Factory be the right choice?No. Any factory (Factory Method, a static creator, a DI container) removes `new`. Abstract Factory earns its extra structure only when several product kinds must vary together and mixing variants must be prevented.
- Where should the decision of which concrete factory to use live?In the composition root — startup wiring, configuration reading, or DI container bindings — so exactly one place knows concrete class names and the rest of the codebase stays variant-agnostic.
A restaurant's set menu. You pick "the vegan menu" once; the kitchen then hands you a starter, a main and a dessert that all belong to that menu. You never assemble a vegan starter with a meat main by accident, because one decision produced the whole tray.
saying these in an interview costs you the question
- Saying Abstract Factory is just "a factory with an interface" and never mentioning families or consistency.
- Claiming it removes all coupling to concrete classes — the composition root must still choose one.
- Letting client code downcast the returned abstract product back to a concrete type, which destroys the whole benefit.
- Introducing it when there is exactly one variant and no realistic second one — pure speculative generality.
- Confusing it with Builder (step-by-step construction of one complex object) or Prototype (cloning).