Abstract Factory Design Pattern in Java

1. Definition

The Abstract Factory Design Pattern is a creational pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes. It involves multiple Factory Methods, one for each type of object to be created.

2. Problem Statement

Imagine you're building a UI library that should be adaptable to both dark and light themes. For each theme, you have different implementations of components like buttons, text boxes, and sliders. If you use simple factories, you'll end up with multiple factories for each component and theme combination. This can quickly become unwieldy.

3. Solution

The Abstract Factory Pattern offers a solution by providing an interface for creating families of related objects. Using this pattern, you'd have a factory for each theme, and each factory would create all the components for that theme.

4. Real-World Use Cases

1. GUI libraries where different themes or OS have distinct looks and behaviors for components.

2. E-commerce platforms where different regions require different combinations of payment gateways, tax calculations, and shipping rules.

3. Game development where different environments (forest, desert, underwater) might have different sets of characters, obstacles, and backgrounds.

5. Implementation Steps

1. Define a series of product interfaces for a family of related products.

2. Implement concrete product classes for each interface.

3. Create an abstract factory interface with creation methods for all products in the family.

4. Implement concrete factories for each family variant.

6. Implementation

// Step 1: Define product interfaces.
interface Button {
    void click();
}

interface TextBox {
    void type();
}

// Step 2: Implement concrete products.
class DarkButton implements Button {
    @Override
    public void click() {
        System.out.println("Dark themed button clicked!");
    }
}

class LightButton implements Button {
    @Override
    public void click() {
        System.out.println("Light themed button clicked!");
    }
}

class DarkTextBox implements TextBox {
    @Override
    public void type() {
        System.out.println("Typing in dark themed text box.");
    }
}

class LightTextBox implements TextBox {
    @Override
    public void type() {
        System.out.println("Typing in light themed text box.");
    }
}

// Step 3 and 4: Define abstract factory and concrete factories.
interface GUIFactory {
    Button createButton();
    TextBox createTextBox();
}

class DarkThemeFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new DarkButton();
    }

    @Override
    public TextBox createTextBox() {
        return new DarkTextBox();
    }
}

class LightThemeFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new LightButton();
    }

    @Override
    public TextBox createTextBox() {
        return new LightTextBox();
    }
}

// Demonstration
public class AbstractFactoryPatternDemo {
    public static void main(String[] args) {
        GUIFactory factory = new DarkThemeFactory();
        Button button = factory.createButton();
        TextBox textBox = factory.createTextBox();

        button.click();
        textBox.type();

        factory = new LightThemeFactory();
        button = factory.createButton();
        textBox = factory.createTextBox();

        button.click();
        textBox.type();
    }
}

Output:

Dark themed button clicked!
Typing in dark themed text box.
Light themed button clicked!
Typing in light themed text box.

Explanation

In this example:

1. Button and TextBox are product interfaces.

2. DarkButton, LightButton, DarkTextBox, and LightTextBox are concrete implementations of these interfaces.

3. GUIFactory is the abstract factory that declares methods to create each product.

4. DarkThemeFactory and LightThemeFactory are concrete factories that produce a consistent set of products (either dark or light themed).

5. The demonstration shows how, based on which factory is used, the system produces a coherent set of theme-consistent components.

This pattern ensures that the products created by a factory are consistent and compatible with each other, and swapping out one factory for another changes the entire family of products.

7. When to use?

1. When a system needs to create product sets with multiple variations.

2. When a family of related products is designed to be used together, and this constraint must be ensured.

3. When you want to expose only the product creation methods and hide the actual product implementation classes from the client.

The Abstract Factory Design Pattern promotes consistency, code reusability, and system modularity.


Comments