Back to Overview

Abstract Factory Pattern

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. It's particularly useful when your code needs to work with various families of related products, but you don't want it to depend on the concrete classes of those products.

Problem

Imagine you're creating a UI toolkit that needs to work across multiple operating systems. Each operating system requires different UI components (buttons, checkboxes, etc.) with their own look and feel. You want to ensure that the UI components match the operating system style, but you don't want your code tightly coupled to specific UI component classes.

Or consider a furniture shop that sells furniture sets. You want to ensure that the pieces of furniture you create match each other. You don't want to create a colonial-style sofa with a modern chair or table.

AbstractFactory createProductA() createProductB() ConcreteFactory1 ConcreteFactory2 createProductA() createProductB() createProductA() createProductB() AbstractProductA AbstractProductB ProductA1 ProductA2 ProductB1 ProductB2 creates creates creates creates Client uses uses uses Product Family 1 Product Family 2

Implementation Example

Here's a basic implementation of the Abstract Factory pattern in JavaScript for UI components:

// Abstract Products
class Button {
    render() {
        throw new Error("Button.render() must be implemented");
    }
    
    onClick() {
        throw new Error("Button.onClick() must be implemented");
    }
}

class Checkbox {
    render() {
        throw new Error("Checkbox.render() must be implemented");
    }
    
    toggle() {
        throw new Error("Checkbox.toggle() must be implemented");
    }
}

// Concrete Products for Windows
class WindowsButton extends Button {
    render() {
        return '<button class="windows-button">Windows Button</button>';
    }
    
    onClick() {
        return "Windows button clicked";
    }
}

class WindowsCheckbox extends Checkbox {
    render() {
        return '<div class="windows-checkbox"><input type="checkbox"></div>';
    }
    
    toggle() {
        return "Windows checkbox toggled";
    }
}

// Concrete Products for MacOS
class MacOSButton extends Button {
    render() {
        return '<button class="macos-button">MacOS Button</button>';
    }
    
    onClick() {
        return "MacOS button clicked";
    }
}

class MacOSCheckbox extends Checkbox {
    render() {
        return '<div class="macos-checkbox"><input type="checkbox"></div>';
    }
    
    toggle() {
        return "MacOS checkbox toggled";
    }
}

// Abstract Factory
class GUIFactory {
    createButton() {
        throw new Error("GUIFactory.createButton() must be implemented");
    }
    
    createCheckbox() {
        throw new Error("GUIFactory.createCheckbox() must be implemented");
    }
}

// Concrete Factories
class WindowsFactory extends GUIFactory {
    createButton() {
        return new WindowsButton();
    }
    
    createCheckbox() {
        return new WindowsCheckbox();
    }
}

class MacOSFactory extends GUIFactory {
    createButton() {
        return new MacOSButton();
    }
    
    createCheckbox() {
        return new MacOSCheckbox();
    }
}

// Client Code
class Application {
    constructor(factory) {
        this.factory = factory;
        this.button = null;
        this.checkbox = null;
    }
    
    createUI() {
        this.button = this.factory.createButton();
        this.checkbox = this.factory.createCheckbox();
    }
    
    paint() {
        return `
            <div>
                ${this.button.render()}
                ${this.checkbox.render()}
            </div>
        `;
    }
}

// Application configuration
function configureApplication() {
    const os = getOperatingSystem(); // This would detect the OS in a real app
    let factory;
    
    if (os === "Windows") {
        factory = new WindowsFactory();
    } else if (os === "MacOS") {
        factory = new MacOSFactory();
    } else {
        throw new Error("Unknown operating system");
    }
    
    const app = new Application(factory);
    app.createUI();
    return app;
}

// In this example, we'll just hardcode the OS
function getOperatingSystem() {
    return "Windows";
}

// Run the application
const app = configureApplication();
const ui = app.paint();

Components

When to Use

Benefits

Real-World Uses

Interactive Demo: Furniture Factory

This demo demonstrates how the Abstract Factory pattern allows you to create families of related objects. Select a furniture style to see how different furniture pieces are created that match the same style.

// Abstract Factory demonstration log will appear here