Abstract Factory
Intent
Section titled “Intent”Abstract Factory provides one interface for creating a family of related products without naming their concrete classes, guaranteeing the products you get always belong to the same family.
Problem
Section titled “Problem”Consider a cross-platform UI kit. A window needs a button and a checkbox, and on every platform those widgets must look and behave consistently — a macOS button paired with a Windows checkbox would be wrong. The danger is mixing families: code that creates each widget independently can easily assemble an inconsistent set.
Abstract Factory solves this by promoting the whole family to a single interface. A GuiFactory declares createButton and createCheckbox; each concrete factory (MacFactory, WindowsFactory) returns widgets from exactly one platform. The application receives a factory once, then asks it for parts. Because every part comes from the same factory, they are guaranteed to match, and switching platforms means swapping one object.
Contrast with Factory Method. Factory Method is about one product chosen by subclassing and overriding a single method; the variation rides on inheritance. Abstract Factory is about a family of products selected by composing in a factory object; you typically hold a factory instance and call several creation methods on it. Put differently: Factory Method answers “which one?” through a subclass, while Abstract Factory answers “which set?” through a swappable object — and an Abstract Factory’s methods are often themselves factory methods.
Structure
Section titled “Structure”classDiagram
class GuiFactory {
<<interface>>
+createButton() Button
+createCheckbox() Checkbox
}
class MacFactory {
+createButton() Button
+createCheckbox() Checkbox
}
class WindowsFactory {
+createButton() Button
+createCheckbox() Checkbox
}
class Button {
<<interface>>
+render() string
}
class Checkbox {
<<interface>>
+render() string
}
class MacButton
class WindowsButton
class MacCheckbox
class WindowsCheckbox
GuiFactory <|.. MacFactory
GuiFactory <|.. WindowsFactory
Button <|.. MacButton
Button <|.. WindowsButton
Checkbox <|.. MacCheckbox
Checkbox <|.. WindowsCheckbox
MacFactory ..> MacButton
MacFactory ..> MacCheckbox
WindowsFactory ..> WindowsButton
WindowsFactory ..> WindowsCheckbox - Abstract Factory (
GuiFactory) — declares a creation method per product in the family. - Concrete Factories (
MacFactory,WindowsFactory) — each produces a complete, internally consistent family. - Abstract Products (
Button,Checkbox) — the interfaces the client depends on. - Concrete Products (
MacButton,WindowsCheckbox, …) — the platform-specific implementations. - Client — receives a factory and builds its UI from the parts it returns, never naming a concrete class.
Example
Section titled “Example”A GuiFactory that yields a matching button and checkbox per platform.
interface Button { render(): string;}interface Checkbox { render(): string;}
class MacButton implements Button { render() { return '[ macOS button ]'; }}class MacCheckbox implements Checkbox { render() { return '[ macOS checkbox ]'; }}class WindowsButton implements Button { render() { return '[ Windows button ]'; }}class WindowsCheckbox implements Checkbox { render() { return '[ Windows checkbox ]'; }}
interface GuiFactory { createButton(): Button; createCheckbox(): Checkbox;}
class MacFactory implements GuiFactory { createButton(): Button { return new MacButton(); } createCheckbox(): Checkbox { return new MacCheckbox(); }}class WindowsFactory implements GuiFactory { createButton(): Button { return new WindowsButton(); } createCheckbox(): Checkbox { return new WindowsCheckbox(); }}
function buildForm(factory: GuiFactory): string { return `${factory.createButton().render()} ${factory.createCheckbox().render()}`;}
console.log(buildForm(new MacFactory()));console.log(buildForm(new WindowsFactory()));from abc import ABC, abstractmethod
class Button(ABC): @abstractmethod def render(self) -> str: ...
class Checkbox(ABC): @abstractmethod def render(self) -> str: ...
class MacButton(Button): def render(self) -> str: return "[ macOS button ]"
class MacCheckbox(Checkbox): def render(self) -> str: return "[ macOS checkbox ]"
class WindowsButton(Button): def render(self) -> str: return "[ Windows button ]"
class WindowsCheckbox(Checkbox): def render(self) -> str: return "[ Windows checkbox ]"
class GuiFactory(ABC): @abstractmethod def create_button(self) -> Button: ...
@abstractmethod def create_checkbox(self) -> Checkbox: ...
class MacFactory(GuiFactory): def create_button(self) -> Button: return MacButton()
def create_checkbox(self) -> Checkbox: return MacCheckbox()
class WindowsFactory(GuiFactory): def create_button(self) -> Button: return WindowsButton()
def create_checkbox(self) -> Checkbox: return WindowsCheckbox()
def build_form(factory: GuiFactory) -> str: return f"{factory.create_button().render()} {factory.create_checkbox().render()}"
print(build_form(MacFactory()))print(build_form(WindowsFactory()))package main
import "fmt"
type Button interface{ Render() string }type Checkbox interface{ Render() string }
type MacButton struct{}
func (MacButton) Render() string { return "[ macOS button ]" }
type MacCheckbox struct{}
func (MacCheckbox) Render() string { return "[ macOS checkbox ]" }
type WindowsButton struct{}
func (WindowsButton) Render() string { return "[ Windows button ]" }
type WindowsCheckbox struct{}
func (WindowsCheckbox) Render() string { return "[ Windows checkbox ]" }
type GuiFactory interface { CreateButton() Button CreateCheckbox() Checkbox}
type MacFactory struct{}
func (MacFactory) CreateButton() Button { return MacButton{} }func (MacFactory) CreateCheckbox() Checkbox { return MacCheckbox{} }
type WindowsFactory struct{}
func (WindowsFactory) CreateButton() Button { return WindowsButton{} }func (WindowsFactory) CreateCheckbox() Checkbox { return WindowsCheckbox{} }
func buildForm(f GuiFactory) string { return f.CreateButton().Render() + " " + f.CreateCheckbox().Render()}
func main() { fmt.Println(buildForm(MacFactory{})) fmt.Println(buildForm(WindowsFactory{}))}trait Button { fn render(&self) -> String;}trait Checkbox { fn render(&self) -> String;}
struct MacButton;impl Button for MacButton { fn render(&self) -> String { "[ macOS button ]".to_string() }}struct MacCheckbox;impl Checkbox for MacCheckbox { fn render(&self) -> String { "[ macOS checkbox ]".to_string() }}struct WindowsButton;impl Button for WindowsButton { fn render(&self) -> String { "[ Windows button ]".to_string() }}struct WindowsCheckbox;impl Checkbox for WindowsCheckbox { fn render(&self) -> String { "[ Windows checkbox ]".to_string() }}
trait GuiFactory { fn create_button(&self) -> Box<dyn Button>; fn create_checkbox(&self) -> Box<dyn Checkbox>;}
struct MacFactory;impl GuiFactory for MacFactory { fn create_button(&self) -> Box<dyn Button> { Box::new(MacButton) } fn create_checkbox(&self) -> Box<dyn Checkbox> { Box::new(MacCheckbox) }}
struct WindowsFactory;impl GuiFactory for WindowsFactory { fn create_button(&self) -> Box<dyn Button> { Box::new(WindowsButton) } fn create_checkbox(&self) -> Box<dyn Checkbox> { Box::new(WindowsCheckbox) }}
fn build_form(factory: &dyn GuiFactory) -> String { format!( "{} {}", factory.create_button().render(), factory.create_checkbox().render() )}
fn main() { println!("{}", build_form(&MacFactory)); println!("{}", build_form(&WindowsFactory));}When to use / trade-offs
Section titled “When to use / trade-offs”- Pro: products from one factory are guaranteed to belong to the same family — no mismatched combinations.
- Pro: swapping the entire family is a one-line change: pass a different factory.
- Pro: the client depends only on abstract product interfaces, never concrete classes.
- Con: adding a new product to the family means changing the factory interface and every concrete factory.
- Con: for a single product or single family it is heavier than Factory Method or a plain constructor.
Related patterns
Section titled “Related patterns”- Factory Method is the simpler one-product cousin and often implements each method of an Abstract Factory.
- Singleton frequently holds the chosen factory so one instance is shared app-wide.