Abstract Factory Pattern in Java: Families of Related Objects
The Problem
Sometimes you need to create families of related objects that must stay consistent with each other — for example, UI components for different themes, or payment gateway objects for different regions. Abstract Factory provides an interface for creating families of related objects without specifying their concrete classes.
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 BillingComponentFactory {
<<interface>>
+createInvoiceGenerator() InvoiceGenerator
+createPaymentProcessor() PaymentProcessor
}
class PrepaidFactory {
+createInvoiceGenerator() InvoiceGenerator
+createPaymentProcessor() PaymentProcessor
}
class PostpaidFactory {
+createInvoiceGenerator() InvoiceGenerator
+createPaymentProcessor() PaymentProcessor
}
class InvoiceGenerator {
<<interface>>
}
class PaymentProcessor {
<<interface>>
}
BillingComponentFactory <|.. PrepaidFactory
BillingComponentFactory <|.. PostpaidFactory
BillingComponentFactory ..> InvoiceGenerator
BillingComponentFactory ..> PaymentProcessorJava Example
public interface InvoiceGenerator { String generate(); }
public interface PaymentProcessor { void charge(double amount); }
public class PrepaidInvoiceGenerator implements InvoiceGenerator {
public String generate() { return "Prepaid receipt"; }
}
public class PrepaidPaymentProcessor implements PaymentProcessor {
public void charge(double amount) { System.out.println("Deducting balance: " + amount); }
}
public class PostpaidInvoiceGenerator implements InvoiceGenerator {
public String generate() { return "Postpaid invoice"; }
}
public class PostpaidPaymentProcessor implements PaymentProcessor {
public void charge(double amount) { System.out.println("Charging card: " + amount); }
}
public interface BillingComponentFactory {
InvoiceGenerator createInvoiceGenerator();
PaymentProcessor createPaymentProcessor();
}
public class PrepaidFactory implements BillingComponentFactory {
public InvoiceGenerator createInvoiceGenerator() { return new PrepaidInvoiceGenerator(); }
public PaymentProcessor createPaymentProcessor() { return new PrepaidPaymentProcessor(); }
}
public class PostpaidFactory implements BillingComponentFactory {
public InvoiceGenerator createInvoiceGenerator() { return new PostpaidInvoiceGenerator(); }
public PaymentProcessor createPaymentProcessor() { return new PostpaidPaymentProcessor(); }
}Quarkus Example
CDI qualifiers make Abstract Factory very clean — each family is a set of beans tagged with a common qualifier, and a single injection point resolves the whole family:
@Qualifier
@Retention(RUNTIME)
@Target({FIELD, METHOD, PARAMETER})
public @interface BillingMode {
Mode value();
enum Mode { PREPAID, POSTPAID }
}
@ApplicationScoped
@BillingMode(BillingMode.Mode.PREPAID)
public class PrepaidInvoiceGenerator implements InvoiceGenerator { /* ... */ }
@ApplicationScoped
@BillingMode(BillingMode.Mode.POSTPAID)
public class PostpaidInvoiceGenerator implements InvoiceGenerator { /* ... */ }
@ApplicationScoped
public class BillingResource {
@Inject
@BillingMode(BillingMode.Mode.PREPAID)
InvoiceGenerator prepaidInvoiceGenerator;
}Key Takeaways
- Use Abstract Factory when families of objects must be created together and stay compatible (e.g., all prepaid components, all postpaid components).
- It’s a step up from Factory Method: instead of one product, you produce a coherent set.
- In Quarkus/CDI, custom qualifiers +
@ApplicationScopedbeans are the idiomatic way to implement product families — very useful in BSS/telco billing systems with prepaid/postpaid or per-tenant variants.

