Abstract Factory Pattern in Java

Illustration for Abstract Factory Pattern in Java
By Last updated:

Introduction

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.

Why Abstract Factory Matters

Imagine you're building a cross-platform UI framework. Your app should create Windows and Mac buttons, checkboxes, and scrollbars. Instead of using conditionals everywhere, you use an Abstract Factory that returns all UI components tailored to the target OS.

This pattern is widely used in GUI libraries, game development, and frameworks where different variants of objects need to be instantiated together.


Core Intent and Participants

  • Intent: Provide an interface for creating families of related or dependent objects without specifying their concrete classes.

Participants

  • AbstractFactory: Declares creation methods for product families.
  • ConcreteFactory: Implements the creation methods for specific variants.
  • AbstractProduct: Common interface for a type of product.
  • ConcreteProduct: Variant of the product.
  • Client: Uses factories and products via their abstract interfaces.

UML Diagram (Text)

+------------------------+          +------------------------+
|    AbstractFactory     |<>--------|    AbstractProductA    |
|------------------------|          +------------------------+
| + createProductA()     |          +------------------------+
| + createProductB()     |                ▲
+------------------------+                |
        ▲                                 |
        |                        +------------------------+
+------------------------+       |   ConcreteProductA1    |
|   ConcreteFactory1     |       +------------------------+
|------------------------|
| + createProductA()     |
| + createProductB()     |
+------------------------+

Real-World Use Cases

  • UI toolkit (MacOS, Windows, Linux themes)
  • Theme switching (Dark mode vs Light mode)
  • Game development (different enemies/weapons per level)
  • Database driver switching (Oracle vs MySQL dialects)
  • Payment gateways (Razorpay vs Stripe)

Implementation in Java

Let’s build a UI framework that renders buttons and checkboxes for Windows and MacOS.

Step 1: Abstract Products

public interface Button {
    void paint();
}

public interface Checkbox {
    void render();
}

Step 2: Concrete Products

public class WindowsButton implements Button {
    public void paint() {
        System.out.println("Rendering a Windows Button");
    }
}

public class MacButton implements Button {
    public void paint() {
        System.out.println("Rendering a Mac Button");
    }
}

public class WindowsCheckbox implements Checkbox {
    public void render() {
        System.out.println("Rendering a Windows Checkbox");
    }
}

public class MacCheckbox implements Checkbox {
    public void render() {
        System.out.println("Rendering a Mac Checkbox");
    }
}

Step 3: Abstract Factory

public interface GUIFactory {
    Button createButton();
    Checkbox createCheckbox();
}

Step 4: Concrete Factories

public class WindowsFactory implements GUIFactory {
    public Button createButton() {
        return new WindowsButton();
    }
    public Checkbox createCheckbox() {
        return new WindowsCheckbox();
    }
}

public class MacFactory implements GUIFactory {
    public Button createButton() {
        return new MacButton();
    }
    public Checkbox createCheckbox() {
        return new MacCheckbox();
    }
}

Step 5: Client Code

public class Application {
    private Button button;
    private Checkbox checkbox;

    public Application(GUIFactory factory) {
        button = factory.createButton();
        checkbox = factory.createCheckbox();
    }

    public void renderUI() {
        button.paint();
        checkbox.render();
    }
}

Step 6: Main Method

public class Demo {
    public static void main(String[] args) {
        GUIFactory factory = new MacFactory();
        Application app = new Application(factory);
        app.renderUI();
    }
}

✅ The client code is unaware of which concrete classes are being used.


Pros and Cons

✅ Pros

  • Enforces consistency among related objects
  • Promotes separation of concerns
  • Makes it easy to swap product families

❌ Cons

  • Complexity increases with object families
  • Adding new product types requires changing all factories

Anti-Patterns and Misuse

  • Using Abstract Factory when products aren’t truly related
  • Not using interfaces, leading to tight coupling
  • Overengineering for simple use cases

Factory vs Abstract Factory vs Builder

Pattern Purpose Product Scope Complexity Use Case
Factory Method Create one object Single product Low Notification types, DAO factories
Abstract Factory Create families of related objects Multiple products Medium UI toolkits, Theming engines
Builder Step-by-step complex object creation One complex object Medium JSON/XML parsers, Car configuration

Refactoring Legacy Code

Before (Spaghetti Code):

if (os.equals("Windows")) {
    button = new WindowsButton();
    checkbox = new WindowsCheckbox();
} else {
    button = new MacButton();
    checkbox = new MacCheckbox();
}

After (Clean Factory Use):

GUIFactory factory = os.equals("Windows") ? new WindowsFactory() : new MacFactory();
Application app = new Application(factory);
app.renderUI();

✅ Easier to extend and maintain.


Best Practices

  • Use interfaces for all products and factories
  • Apply when consistency between products matters
  • Combine with Dependency Injection for flexibility
  • Prefer composition over inheritance

Real-World Analogy

Think of a Furniture Factory. IKEA can have a VictorianFactory that makes a Victorian-style sofa and table, and a ModernFactory that produces a minimalist sofa and table. The style remains consistent across all items in each set.


Java Version Relevance

  • Java 8+: Use lambdas with factories (Supplier<T>)
  • Java 17+: Use sealed classes to restrict product hierarchy

Conclusion & Key Takeaways

  • Abstract Factory promotes consistency across families of related objects.
  • Keeps object creation decoupled and scalable.
  • Ideal for theme, OS, or platform-based app designs.
  • Beware of unnecessary abstraction.

FAQ – Abstract Factory Pattern in Java

1. What is the Abstract Factory Pattern?

It provides an interface to create families of related objects without specifying concrete classes.

2. When should I use it?

When products in your system need to be consistent in style or configuration.

3. How is it different from Factory Method?

Factory Method creates a single product. Abstract Factory creates related families.

4. Is it a creational pattern?

Yes, part of the GoF creational design patterns.

5. Is this pattern used in Spring?

Yes, especially in bean factories and application contexts.

6. Can I combine it with Dependency Injection?

Yes, use it to decouple creation logic further.

7. Is it test-friendly?

Yes, mock factories can be injected during testing.

8. What are the trade-offs?

Extra complexity when not needed. Don’t use it for simple object creation.

9. Can this pattern work with reflection?

Yes, factories can use reflection to create products dynamically.

10. What’s a real-world use case?

Switching UI components for different platforms (Mac, Windows, Web).