DEV Community

Cover image for Learn Design Patterns: Understanding the Abstract Factory Pattern
Sami Maachi
Sami Maachi

Posted on

Learn Design Patterns: Understanding the Abstract Factory Pattern

Design patterns are essential for building scalable and maintainable software. One creational design pattern, the Abstract Factory Pattern ,stands out as a way to manage families of related objects without being tightly coupled to their concrete implementations.

This article will explore the Abstract Factory Pattern, understand its significance through a real-world scenario, and demonstrate its implementation using TypeScript.

Table of Contents

  1. Abstract Factory Definition

  2. Scenario: Understanding the Problem

  3. Real-Life Projects That Use the Abstract Factory

Abstract Factory Definition

The Abstract Factory Pattern is a creational design pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes.

Instead of relying on direct instantiation, this pattern abstracts the object creation process, ensuring that all objects created belong to a specific family.

Scenario: Understanding the Problem

Imagine we are building a cross-platform UI toolkit. the toolkit must support multiple platforms — Windows, macOS, and Linux.

Each platform has its own UI components, such as buttons, checkboxes, and dropdown menus. For example:

  • Windows has a WindowsButton, WindowsCheckbox, and WindowsDropdown.

  • macOS has a MacButton, MacCheckbox, and MacDropdown.

  • Linux has a LinuxButton, LinuxCheckbox, and LinuxDropdown.

The Challenges

  1. Managing Families of Related Objects
    When switching between platforms, we need to ensure that all components belong to the same platform family. You don’t want of mix different UI components like WindowsButton with MacCheckbox.

  2. Extensibility for New Platforms
    Adding support for a new platform, such as Android, should be straightforward without breaking existing code.

  3. Decoupling Object Creation from Client Code
    The client code (your UI rendering logic) shouldn’t be tied to specific classes like WindowsButton or MacCheckbox.

What Do We Want Instead?

We need a solution that:

  • Centralizes Object Creation: Ensures components are created consistently for each platform.

  • Supports Families of Objects: All UI components for a specific platform (e.g., buttons, checkboxes) should be grouped logically.

  • Simplifies Adding New Platforms: Adding support for Android or other platforms should require minimal changes to the codebase.

This is where the Abstract Factory Pattern excels.

Let’s implement the Abstract Factory Pattern for our cross-platform UI toolkit.

Step 1: Define Abstract Product Interfaces

interface Button {
  render(): void;
}

interface Checkbox {
  render(): void;
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Implement Concrete Products

class WindowsButton implements Button {
  render(): void {
    console.log("Rendering Windows Button");
  }
}

class MacButton implements Button {
  render(): void {
    console.log("Rendering Mac Button");
  }
}

class WindowsCheckbox implements Checkbox {
  render(): void {
    console.log("Rendering Windows Checkbox");
  }
}

class MacCheckbox implements Checkbox {
  render(): void {
    console.log("Rendering Mac Checkbox");
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Define Abstract Factory

interface UIFactory {
  createButton(): Button;
  createCheckbox(): Checkbox;
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Implement Concrete Factories

class WindowsFactory implements UIFactory {
  createButton(): Button {
    return new WindowsButton();
  }

createCheckbox(): Checkbox {
    return new WindowsCheckbox();
  }
}

class MacFactory implements UIFactory {
  createButton(): Button {
    return new MacButton();
  }
  createCheckbox(): Checkbox {
    return new MacCheckbox();
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Client Code

function renderUI(factory: UIFactory): void {
  const button = factory.createButton();
  const checkbox = factory.createCheckbox();
  button.render();
  checkbox.render();
}
Enter fullscreen mode Exit fullscreen mode

Step 6: Using the Factories

console.log("Rendering Windows UI:");
renderUI(new WindowsFactory());

console.log("\nRendering Mac UI:");
renderUI(new MacFactory());
Enter fullscreen mode Exit fullscreen mode

Output:

Rendering Windows UI:
Rendering Windows Button
Rendering Windows Checkbox
Enter fullscreen mode Exit fullscreen mode




Real-Life Projects That Use the Abstract Factory

  1. Cross-Platform Frameworks
    Frameworks like Qt or Flutter use the Abstract Factory Pattern to render UI components across platforms like Windows, macOS, and Linux.

  2. Database Connection Managers
    Abstract factories can create database connections for different databases (e.g., MySQL, PostgreSQL, MongoDB), ensuring that related components like query builders and transactions work consistently.

  3. Theming Systems
    The Abstract Factory Pattern is used to create UI components for specific themes (e.g., light mode, dark mode).

Conclusion

The Abstract Factory Pattern is a versatile design pattern that simplifies managing families of related objects while promoting scalability and maintainability. It’s particularly useful in scenarios involving cross-platform development, theming, or any situation where object families must remain consistent.

In the next article of our Learn Design Patterns series, we’ll explore the Builder Pattern, which focuses on constructing complex objects step by step. Stay tuned, and keep learning!

Top comments (0)