Java > Design Patterns in Java > Creational Patterns > Abstract Factory

Abstract Factory Pattern Example: GUI Factory

This example demonstrates the Abstract Factory pattern by creating GUI elements (buttons and text boxes) for different operating systems (Windows and macOS). The Abstract Factory provides an interface for creating families of related objects without specifying their concrete classes. This promotes flexibility and avoids tight coupling to specific implementations.

Defining the Abstract Products

First, we define the abstract product interfaces: `Button` and `TextBox`. These interfaces declare the common operations that all concrete buttons and text boxes must implement.

Button Interface

The `Button` interface defines the `paint()` method, which is responsible for rendering the button on the screen.

interface Button {
    void paint();
}

TextBox Interface

The `TextBox` interface defines the `display()` method, which is responsible for rendering the text box on the screen.

interface TextBox {
    void display();
}

Concrete Product Implementations

Next, we create concrete implementations of the `Button` and `TextBox` interfaces for Windows and macOS. Each concrete product is specific to a particular operating system.

Windows Button

The `WindowsButton` class implements the `Button` interface and renders a Windows-style button.

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

macOS Button

The `MacOSButton` class implements the `Button` interface and renders a macOS-style button.

class MacOSButton implements Button {
    @Override
    public void paint() {
        System.out.println("Rendering a macOS button.");
    }
}

Windows TextBox

The `WindowsTextBox` class implements the `TextBox` interface and renders a Windows-style text box.

class WindowsTextBox implements TextBox {
    @Override
    public void display() {
        System.out.println("Displaying a Windows text box.");
    }
}

macOS TextBox

The `MacOSTextBox` class implements the `TextBox` interface and renders a macOS-style text box.

class MacOSTextBox implements TextBox {
    @Override
    public void display() {
        System.out.println("Displaying a macOS text box.");
    }
}

Abstract Factory Interface

Now, we define the abstract factory interface, `GUIFactory`. This interface declares the methods for creating the abstract products (buttons and text boxes).

GUIFactory Interface

The `GUIFactory` interface defines the `createButton()` and `createTextBox()` methods, which are responsible for creating concrete buttons and text boxes, respectively.

interface GUIFactory {
    Button createButton();
    TextBox createTextBox();
}

Concrete Factory Implementations

We create concrete factory implementations for Windows and macOS. Each concrete factory is responsible for creating the appropriate concrete products for its operating system.

Windows Factory

The `WindowsFactory` class implements the `GUIFactory` interface and creates Windows-style buttons and text boxes.

class WindowsFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new WindowsButton();
    }

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

macOS Factory

The `MacOSFactory` class implements the `GUIFactory` interface and creates macOS-style buttons and text boxes.

class MacOSFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new MacOSButton();
    }

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

Client Code

The `AbstractFactoryDemo` class demonstrates how to use the Abstract Factory pattern. It creates a GUI factory based on the operating system and then uses the factory to create buttons and text boxes. The `createFactory()` method determines which concrete factory to use based on the operating system.

public class AbstractFactoryDemo {
    public static void main(String[] args) {
        GUIFactory factory = createFactory();
        Button button = factory.createButton();
        TextBox textBox = factory.createTextBox();

        button.paint();
        textBox.display();
    }

    static GUIFactory createFactory() {
        String os = System.getProperty("os.name").toLowerCase();
        if (os.contains("win")) {
            return new WindowsFactory();
        } else {
            return new MacOSFactory();
        }
    }
}

Concepts Behind the Snippet

The core idea is to abstract the creation process of related objects. This promotes loose coupling and allows for easy switching between families of products (e.g., changing the look and feel of the UI by switching the factory). The key concepts are: * Abstract Factory: The interface for creating abstract products. * Concrete Factories: Implementations of the abstract factory that create concrete products. * Abstract Products: Interfaces for related products. * Concrete Products: Implementations of the abstract products.

Real-Life Use Case

A common use case is creating UI toolkits for different platforms. Imagine a cross-platform application that needs to render UI elements differently depending on the operating system. The Abstract Factory pattern allows you to create platform-specific UI components without tightly coupling the application logic to the specific UI implementations. Other use cases include document processing (creating different types of documents based on format) and game development (creating different character types based on the game level).

Best Practices

  • Favor Composition over Inheritance: The Abstract Factory relies on object composition (using interfaces and abstract classes) rather than inheritance.
  • Single Responsibility Principle: Each factory should be responsible for creating a specific family of related objects.
  • Open/Closed Principle: You should be able to add new product families without modifying existing code.

Interview Tip

Be prepared to explain the benefits of using the Abstract Factory pattern, such as decoupling and flexibility. Also, be ready to discuss when it is appropriate to use this pattern (e.g., when you need to create families of related objects that are used together).

When to Use Them

Use the Abstract Factory pattern when: * A system should be independent of how its products are created, composed, and represented. * A system should be configured with one of multiple families of products. * A family of related product objects is designed to be used together, and you need to enforce this constraint. * You want to provide a class library of products, and you want to reveal just their interfaces, not their implementations.

Memory Footprint

The memory footprint of the Abstract Factory pattern depends on the number of factories and products you create. Each factory and product instance will consume memory. However, the pattern itself does not introduce significant overhead. Consider the number of concrete factories and products to be instantiated based on your application needs, and whether or not they are long-lived objects.

Alternatives

Alternatives to the Abstract Factory pattern include: * Factory Method: Simpler to implement when you only need to vary the concrete class that is instantiated. * Builder: Used for constructing complex objects step-by-step. * Prototype: Used for creating new objects by cloning existing objects.

Pros

  • Isolates concrete classes: The client is isolated from the concrete product classes.
  • Exchanges product families easily: A concrete factory appears only once in an application, which allows you to easily change the product family used in the entire application.
  • Promotes consistency: The pattern ensures that products from the same family are used together.

Cons

  • Adding new products is difficult: Adding a new type of product requires modifying the abstract factory interface and all its concrete implementations, which can be cumbersome.
  • Can increase complexity: The pattern can introduce additional complexity to the code, especially when the number of product families and products is large.

FAQ

  • When should I use Abstract Factory vs. Factory Method?

    Use Abstract Factory when you need to create families of related objects. Use Factory Method when you need to create a single type of object and want to decouple the object creation from the client code.
  • Can Abstract Factory be combined with other creational patterns?

    Yes, Abstract Factory can be combined with other creational patterns, such as Builder or Prototype, to create more complex object creation processes.