Posts

Abstract Factory

June 13, 2025
Abstract Factory Pattern
The Abstract Factory Pattern lets you create families of related objects (e.g., Windows buttons + checkboxes, or Mac buttons + checkboxes) without specifying their concrete classes.
  • You want consistent-looking components (like GUI widgets) that match a theme (e.g., Windows or Mac).
  • You want to switch between product families without touching your app's main logic.
  • You want your code to depend on interfaces, not concrete implementations.

Think of a furniture set. You don’t want to mix a Victorian chair with a Modern table. You’d want your chair, table, and sofa to all come from the same family (style).
  1. Define abstract interfaces (Button, Checkbox)
  2. Implement concrete products (WinButton, MacButton, etc.)
  3. Create factory interface (GUIFactory)
  4. Implement concrete factories (WinFactory, MacFactory)
  5. Let application code use only the factory interface

Ts
// Abstract Product Interfaces
interface Button {
  paint(): void;
}

interface Checkbox {
  paint(): void;
}

// Concrete Products (Windows style)
class WinButton implements Button {
  paint(): void {
    console.log("Rendered a Windows-style button.");
  }
}

class WinCheckbox implements Checkbox {
  paint(): void {
    console.log("Rendered a Windows-style checkbox.");
  }
}

// Concrete Products (Mac style)
class MacButton implements Button {
  paint(): void {
    console.log("Rendered a Mac-style button.");
  }
}

class MacCheckbox implements Checkbox {
  paint(): void {
    console.log("Rendered a Mac-style checkbox.");
  }
}

// Abstract Factory
interface GUIFactory {
  createButton(): Button;
  createCheckbox(): Checkbox;
}

// Concrete Factory for Windows
class WinFactory implements GUIFactory {
  createButton(): Button {
    return new WinButton();
  }
  createCheckbox(): Checkbox {
    return new WinCheckbox();
  }
}

// Concrete Factory for Mac
class MacFactory implements GUIFactory {
  createButton(): Button {
    return new MacButton();
  }
  createCheckbox(): Checkbox {
    return new MacCheckbox();
  }
}

// Application that uses the factory
class Application {
  private button: Button;
  private checkbox: Checkbox;

  constructor(factory: GUIFactory) {
    this.button = factory.createButton();
    this.checkbox = factory.createCheckbox();
  }

  paint(): void {
    this.button.paint();
    this.checkbox.paint();
  }
}

// Simulate environment-based factory selection
function main(os: "Windows" | "Mac") {
  let factory: GUIFactory;

  if (os === "Windows") {
    factory = new WinFactory();
  } else if (os === "Mac") {
    factory = new MacFactory();
  } else {
    throw new Error("Unsupported OS");
  }

  const app = new Application(factory);
  app.paint();
}

main("Windows");
// Output:
// Rendered a Windows-style button.
// Rendered a Windows-style checkbox.

  • Your app works with related objects that should be used together.
  • You want to switch product families easily (e.g., light/dark themes, Windows/Mac).
  • You want interface-based code with no dependency on concrete classes.

✅ Pros:
  • Ensures product consistency (e.g., UI theme)
  • Keeps code decoupled from specific implementations
  • Easily extendable with new product families
❌ Cons:
  • Can require a lot of extra classes and interfaces
  • Might be overkill for small or unrelated object groups
On this page