Abstract Factory
Intermediate · 11 min read · ▶ live playground · ✦ checkpoint
The Windows settings screen passes review with a rounded Windows button and a Mac-style checkbox. Both components work, but the screen looks assembled from two operating systems because the setup code chose each one independently.
The Abstract Factory pattern provides an interface for creating a family of related products without exposing their concrete classes. A product family is a set of objects designed to work or look consistent together, such as a button and checkbox for one operating system.
One factory becomes the unit of selection. Choosing WinFactory chooses the whole Windows family; choosing MacFactory chooses the whole macOS family. That consistency costs more interfaces and concrete classes, and adding a new product category affects every family.
Before: independent choices produce a mixed family
The settings screen owns two individually valid products. The bug is in their combination: WinButton and MacCheckbox belong to different families.
class diagram
Before: a settings screen mixes two UI families
- 1SettingsScreen chooses each concrete component in its own construction code.
- 2The button comes from the Windows family.
- 3The checkbox comes from the macOS family, creating a valid pair with the wrong match.
- 4Independent choices provide no family-level consistency rule.
Shared product interfaces do not prevent the mix. They guarantee that each object behaves like a button or checkbox, not that two objects share a visual family:
interface Button {
render(): string;
}
interface Checkbox {
render(): string;
}
class WinButton implements Button {
public render(): string {
return "Windows button";
}
}
class MacCheckbox implements Checkbox {
public render(): string {
return "macOS checkbox";
}
}
class SettingsScreen {
private button: Button = new WinButton();
private checkbox: Checkbox = new MacCheckbox();
public render(): string {
return this.button.render() + " | " + this.checkbox.render();
}
}
console.log(new SettingsScreen().render());Windows button | macOS checkbox
Adding comments beside those two new calls would document the rule but would not enforce its boundary. Another screen could repeat the same mismatch tomorrow.
After: choose one factory, receive one family
GUIFactory groups the creation operations that must vary together. Client code receives one factory and asks it for both products, so it never selects a concrete button separately from a concrete checkbox.
class diagram
After: factories create matching UI families
- 1GUIFactory groups the two creation choices that must remain consistent.
- 2Every family factory returns a Button through the shared product contract.
- 3The same factory also returns the matching Checkbox contract.
- 4WinFactory commits both methods to the Windows family.
- 5MacFactory commits both methods to the macOS family.
- 6WinButton is the concrete Windows implementation of Button.
- 7WinCheckbox completes the concrete Windows pair; macOS has a parallel pair.
The diagram follows the Windows products down to their concrete types. MacButton and MacCheckbox form the parallel family in code:
type UIFamily = "Windows" | "macOS";
interface Button {
readonly family: UIFamily;
render(): string;
}
interface Checkbox {
readonly family: UIFamily;
render(): string;
}
class WinButton implements Button {
public readonly family = "Windows";
public render(): string {
return "Windows button";
}
}
class WinCheckbox implements Checkbox {
public readonly family = "Windows";
public render(): string {
return "Windows checkbox";
}
}
class MacButton implements Button {
public readonly family = "macOS";
public render(): string {
return "macOS button";
}
}
class MacCheckbox implements Checkbox {
public readonly family = "macOS";
public render(): string {
return "macOS checkbox";
}
}
interface GUIFactory {
createButton(): Button;
createCheckbox(): Checkbox;
}
class WinFactory implements GUIFactory {
public createButton(): Button {
return new WinButton();
}
public createCheckbox(): Checkbox {
return new WinCheckbox();
}
}
class MacFactory implements GUIFactory {
public createButton(): Button {
return new MacButton();
}
public createCheckbox(): Checkbox {
return new MacCheckbox();
}
}
class SettingsPanel {
private button: Button;
private checkbox: Checkbox;
public constructor(factory: GUIFactory) {
this.button = factory.createButton();
this.checkbox = factory.createCheckbox();
}
public render(): string {
return this.button.render() + " | " + this.checkbox.render();
}
}
const windowsPanel = new SettingsPanel(new WinFactory());
const macPanel = new SettingsPanel(new MacFactory());
console.log(windowsPanel.render());
console.log(macPanel.render());Windows button | Windows checkbox macOS button | macOS checkbox
SettingsPanel depends only on GUIFactory, Button, and Checkbox. It cannot accidentally ask WinFactory for a Mac checkbox because concrete selection is owned by the factory implementation.
That guarantee is architectural rather than magical. A badly written class could claim to implement GUIFactory and return mismatched products. The pattern gives the family rule one implementation boundary, where a focused test can verify it, instead of asking every screen to coordinate concrete classes.
Family consistency means all related products supplied by one concrete factory belong to the same variant. It is the reason these creation methods live together.
How it differs from Factory Method
Factory Method uses inheritance to let a creator subclass override one method that produces one product. Abstract Factory uses composition: a client receives a factory object with several creation methods for a related family.
The names can overlap because a concrete abstract factory's createButton() methods are factory methods in the everyday sense. The design intent separates them:
- Factory Method asks, “Which one product should this creator subclass make?”
- Abstract Factory asks, “Which matching family should this client receive?”
An abstract factory may even implement one of its operations with Factory Method internally. The patterns solve different pressures, so the same design can contain both.
When NOT to use it
If the application creates only buttons, there is no family to coordinate. Factory Method, a small factory function, or direct construction has a lower class cost.
Abstract Factory also adds little when buttons and checkboxes vary independently. If users can freely combine any button theme with any checkbox theme, grouping them into fixed families encodes a rule the product does not have.
The class count grows quickly: each family needs concrete products plus a factory. Adding Slider later also changes GUIFactory, WinFactory, MacFactory, and every future family. Use this pattern when related products genuinely vary together and clients benefit from selecting the set as one unit.
Checkpoint
Answer all three to mark this lesson complete
Abstract Factory keeps a related set consistent. Next, Builder handles a different creation problem: assembling one complex object without a positional constructor puzzle.