# Abstract FactoryA intermediate-level guide to Abstract Factory: before-and-after java code and diagrams for a CS student.

## The Theme Matching Nightmare

Imagine you are building a UI library that supports different visual themes. A "Light Mode" might use soft blue buttons and grey scrollbars, while a "Dark Mode" uses charcoal buttons and dark-grey scrollbars. To keep the application looking professional, these components must always match; there is nothing worse than a bright white button sitting in the middle of a pitch-black window.

As you add more components—checkboxes, sliders, text fields—your instantiation logic starts to rot. You find yourself writing repetitive conditional logic every time you need a new piece of the UI. This "Switch Statements" smell occurs when you use `if/else` or `switch` blocks to decide which concrete class to instantiate based on a type or a configuration string.

```java
class UIComponent {}
class LightButton extends UIComponent {}
class DarkButton extends UIComponent {}
class LightScrollbar extends UIComponent {}
class DarkScrollbar extends UIComponent {}

class UserInterface {
    private String theme;

    public UserInterface(String theme) {
        this.theme = theme;
    }

    // Problem: Constructor bloat and conditional logic everywhere
    // Risk of mixing LightButton with DarkScrollbar due to manual selection
    public UIComponent createButton() {
        if (theme.equals("LIGHT")) {
            return new LightButton();
        } else if (theme.equals("DARK")) {
            return new DarkButton();
        }
        throw new IllegalArgumentException("Unknown theme");
    }

    public UIComponent createScrollbar() {
        if (theme.equals("LIGHT")) {
            return new LightScrollbar();
        } else if (theme.equals("DARK")) {
            return new DarkScrollbar();
        }
        throw new IllegalArgumentException("Unknown theme");
    }
}
```

The problem here is two-fold: first, the `UserInterface` class has grown into a bloated mess of conditional logic. Second, and more dangerously, it creates an opportunity for "mismatched families." Because each method (`createButton`, `createScrollbar`) makes its own independent decision about which theme to use, a small logic error could result in a Light Button being paired with a Dark Scrollbar.

```mermaid
classDiagram
    direction BT
    UIComponent <|.. LightButton
    UIComponent <|.. DarkButton
    UIComponent <|.. LightScrollbar
    UIComponent <|.. DarkScrollbar
    UserInterface ..> UIComponent : creates
    note for UserInterface "Contains repetitive if/else \nlogic for every component"
```

## Solving the Problem with Abstract Factory

To fix this, we apply the Replace Conditional with Polymorphism refactoring at a structural level. We introduce the **Abstract Factory**, a creational pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes.

Instead of asking "What theme is active?" every time we need a button, we ask a specific factory to handle it. If we have a `LightThemeFactory`, it knows how to produce the entire suite of light-themed components correctly and consistently.

```java
interface UIComponent {}
class LightButton implements UIComponent {}
class DarkButton implements UIComponent {}
class LightScrollbar implements UIComponent {}
class DarkScrollbar implements UIComponent {}

// The Abstract Factory interface
interface UIFactory {
    UIComponent createButton();
    UIComponent createScrollbar();
}

class LightThemeFactory implements UIFactory {
    public UIComponent createButton() { return new LightButton(); }
    public UIComponent createScrollbar() { return new LightScrollbar(); }
}

class DarkThemeFactory implements UIFactory {
    public UIComponent createButton() { return new DarkButton(); }
    public UIComponent createScrollbar() { return new DarkScrollbar(); }
}

class UserInterface {
    private final UIFactory factory;

    // Client code is decoupled from concrete implementations
    public UserInterface(UIFactory factory) {
        this.factory = factory;
    }

    public UIComponent createButton() { return factory.createButton(); }
    public UIComponent createScrollbar() { return factory.createScrollbar(); }
}
```

In this refactored version, the client (`UserInterface`) no longer cares about "Light" or "Dark." It only knows that it has an object implementing the `UIFactory` interface. This achieves high cohesion: the logic for *what* to create is moved out of the UI logic and into dedicated factory objects.

```mermaid
classDiagram
    direction BT
    UIFactory <|.. LightThemeFactory
    UIFactory <|.. DarkThemeFactory
    UIComponent <|-- LightButton : implements
    UIComponent <|-- DarkButton : implements
    UIComponent <|-- LightScrollbar : implements
    UIComponent <|-- DarkScrollbar : implements
    LightThemeFactory ..> LightButton : creates
    LightThemeFactory ..> LightScrollbar : creates
    DarkThemeFactory ..> DarkButton : creates
    DarkThemeFactory ..> DarkScrollbar : creates
    UserInterface o-- UIFactory : uses
```

When the application runs, the interaction follows a clear delegation flow. The client asks the factory for a component, and the factory handles the messy details of instantiation.

```mermaid
sequenceDiagram
    participant UI as UserInterface
    participant F as LightThemeFactory
    participant B as LightButton

    UI ->> F: createButton()
    F ->> B: new LightButton()
    B -->> F: return instance
    F -->> UI: return instance
```

### Abstract Factory vs. Factory Method

It is common to confuse these two, but the distinction lies in the "scale" of creation. 

*   **Factory Method** is about a single product. You have one method that returns one type of object (e.g., just a Button).
*   **Abstract Factory** is about families of products. It contains multiple factory methods to ensure all objects created belong to the same compatible group (e.g., Buttons, Scrollbars, and Checkboxes that all match).

## When to Use It, and When Not To

The Abstract Factory pattern is a powerful tool for maintaining consistency in complex systems, but it comes with architectural costs.

**Use it when:**
*   Your system needs to be independent of how its products are created, composed, and represented.
*   You have "families" of related objects that must be used together to ensure the system remains consistent.
*   You want to enforce a rule where certain object combinations are invalid (e.g., preventing a Dark Button from appearing in a Light Theme).

**Do not use it when:**
*   Your product family is small and unlikely to grow. Adding a new component type (like a `ColorPicker`) requires updating the Abstract Factory interface and *every* concrete factory implementation, which can lead to "Shotgun Surgery."
*   You are over-engineering. If your application only ever supports one single theme, adding an entire hierarchy of factories adds unnecessary complexity for no tangible benefit.

## Takeaways

- Use Abstract Factory to ensure that families of related objects are instantiated consistently.
- It eliminates repetitive conditional logic by delegating instantiation to specialized factory objects.
- Avoid it if the product family is simple or if you anticipate frequently adding new types of products to the suite.

## Usage

**Using the Abstract Factory to ensure a consistent family of products**

```java
UserInterface ui = new UserInterface(new DarkThemeFactory());
UIComponent btn = ui.createButton(); // Guaranteed to be DarkButton
```
