02 – Factory Method Pattern

02 – Factory Method Pattern

Factory Method Pattern in Java: Delegating Object Creation

The Problem

You need to create objects, but the exact type to instantiate should be decided by subclasses or configuration — not hardcoded with new. The Factory Method pattern defines an interface for creating an object, letting subclasses decide which class to instantiate.

Diagram

%%{init: {'theme':'base', 'themeVariables': { 'primaryColor': '#eef2ff', 'primaryTextColor': '#1e293b', 'primaryBorderColor': '#818cf8', 'lineColor': '#94a3b8', 'secondaryColor': '#f0fdf4', 'secondaryBorderColor': '#86efac', 'tertiaryColor': '#fff7ed', 'tertiaryBorderColor': '#fdba74', 'background': '#ffffff', 'mainBkg': '#eef2ff', 'secondBkg': '#f0fdf4', 'tertiaryBkg': '#fff7ed', 'noteBkgColor': '#fff7ed', 'noteBorderColor': '#fdba74', 'actorBkg': '#eef2ff', 'actorBorder': '#818cf8' }}}%%
classDiagram
    class NotificationCreator {
        <<abstract>>
        +createNotification() Notification
        +send(String message)
    }
    class EmailNotificationCreator {
        +createNotification() Notification
    }
    class SmsNotificationCreator {
        +createNotification() Notification
    }
    class Notification {
        <<interface>>
        +deliver(String message)
    }
    class EmailNotification
    class SmsNotification

    NotificationCreator <|-- EmailNotificationCreator
    NotificationCreator <|-- SmsNotificationCreator
    NotificationCreator ..> Notification : creates
    Notification <|.. EmailNotification
    Notification <|.. SmsNotification

Java Example

public interface Notification {
    void deliver(String message);
}

public class EmailNotification implements Notification {
    public void deliver(String message) {
        System.out.println("Email: " + message);
    }
}

public class SmsNotification implements Notification {
    public void deliver(String message) {
        System.out.println("SMS: " + message);
    }
}

public abstract class NotificationCreator {
    protected abstract Notification createNotification();

    public void send(String message) {
        Notification notification = createNotification();
        notification.deliver(message);
    }
}

public class EmailNotificationCreator extends NotificationCreator {
    protected Notification createNotification() {
        return new EmailNotification();
    }
}

public class SmsNotificationCreator extends NotificationCreator {
    protected Notification createNotification() {
        return new SmsNotification();
    }
}

Quarkus Example

@ApplicationScoped
public class NotificationFactory {

    @Inject
    @ConfigProperty(name = "notification.channel", defaultValue = "email")
    String channel;

    @Produces
    @ApplicationScoped
    public Notification notification() {
        return switch (channel) {
            case "sms" -> new SmsNotification();
            default -> new EmailNotification();
        };
    }
}

In Quarkus, Factory Method often becomes a CDI @Produces method, letting the container pick the implementation based on configuration:

Any bean can then simply @Inject Notification notification; without knowing which concrete implementation is active — Quarkus resolves it from application.properties.

Key Takeaways

  • Use Factory Method when a class can’t anticipate which concrete type it needs to create.
  • It keeps client code decoupled from concrete classes, easing testing and extension.
  • In Quarkus, @Produces methods and CDI qualifiers are a natural, idiomatic replacement for manual factory hierarchies — very common in TMF-style multitenant systems where the implementation varies per tenant or channel.