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. This pattern is useful when a system needs to be independent of how its objects are created, composed, and represented, especially when there are multiple families of products involved.

Applicability

  • You need to ensure consistency among products used together
  • You want to isolate client code from concrete classes
  • You have multiple families of products and need to switch between them

Pros and Cons

Advantages

  • Ensures consistency among related products
    • Products created by a factory are guaranteed to be compatible with each other.
  • Isolates concrete classes
    • The client works only with interfaces or abstract classes, not specific implementations.
  • Supports product variants easily
    • Easily switch between product families by changing the factory object.
  • Encourages separation of concerns
    • Object creation logic is kept separate from business logic.

Drawbacks

  • **Adding new product types is hard
    • If you need a new product in each family, you must update every factory class.
  • Can result in many small classes
    • Each product family and factory needs its own set of interfaces and classes.

Approach

  1. Define Abstract Product interfaces for each kind of product.
  2. Define Concrete Products that implement the abstract product interfaces.
  3. Define an Abstract Factory interface with creation methods for each abstract product.
  4. Define Concrete Factories that implement the abstract factory interface, producing specific families of products.
  5. Clients use the abstract factory to create products without knowing their concrete types.

Components

  • AbstractProductA, AbstractProductB
    • Interfaces for a family of related products.
  • ConcreteProductA1, ConcreteProductA2, etc.
    • Concrete implementations of the abstract products.
  • AbstractFactory
    • Declares creation methods for each type of product.
  • ConcreteFactory1, ConcreteFactory2
    • Implement the creation methods to return product variants.
classDiagram
    class AbstractProductA {
        <<abstract>>
        +operation()
    }

    class ConcreteProductA1 {
        +operation()
    }

    class AbstractProductB {
        <<abstract>>
        +operation()
    }

    class ConcreteProductB1 {
        +operation()
    }

    class AbstractFactory {
        <<abstract>>
        +create_product_a(): AbstractProductA
        +create_product_b(): AbstractProductB
    }

    class ConcreteFactory1 {
        +create_product_a(): ConcreteProductA1
        +create_product_b(): ConcreteProductB1
    }

    AbstractProductA <|-- ConcreteProductA1
    AbstractProductB <|-- ConcreteProductB1
    AbstractFactory <|-- ConcreteFactory1
    ConcreteFactory1 --> ConcreteProductA1 : creates
    ConcreteFactory1 --> ConcreteProductB1 : creates

Example

Let’s say we’re building a UI toolkit that should support multiple operating systems like Windows and macOS. Each OS has its own set of GUI components.

Abstract factory

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

Concrete factory

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

Abstract product

interface Button {
    void paint();
}
 
interface Checkbox {
    void paint();
}

Concrete product

class WindowsButton implements Button {
    public void paint() {
        System.out.println("Rendering a Windows-style button.");
    }
}
 
class MacOSButton implements Button {
    public void paint() {
        System.out.println("Rendering a macOS-style button.");
    }
}
 
class WindowsCheckbox implements Checkbox {
    public void paint() {
        System.out.println("Rendering a Windows-style checkbox.");
    }
}
 
class MacOSCheckbox implements Checkbox {
    public void paint() {
        System.out.println("Rendering a macOS-style checkbox.");
    }
}

Client

class Application {
    private Button button;
    private Checkbox checkbox;
 
    public Application(GUIFactory factory) {
        button = factory.createButton();
        checkbox = factory.createCheckbox();
    }
 
    public void render() {
        button.paint();
        checkbox.paint();
    }
}

Initialisation

public class AbstractFactoryExample {
    public static void main(String[] args) {
        GUIFactory factory;
        String os = System.getProperty("os.name").toLowerCase();
        
        if (os.contains("mac")) {
            factory = new MacOSFactory();
        } else {
            factory = new WindowsFactory();
        }
 
        Application app = new Application(factory);
        app.render();
    }
}
OUTPUT (on Windows):
Rendering a Windows-style button.
Rendering a Windows-style checkbox.

OUTPUT (on macOS):
Rendering a macOS-style button.
Rendering a macOS-style checkbox.

Back to parent page: Creational Patterns

Design_Pattern Creational_Design_Patterns Abstract_Factory_Pattern SOFT2201