Incredible – 04 Builder Pattern

Incredible – 04 Builder Pattern

Builder Pattern in Java: Constructing Complex Objects Step by Step

The Problem

When an object has many optional fields, telescoping constructors (new Foo(a, b, null, null, c, ...)) become unreadable and error-prone. The Builder pattern separates the construction of a complex object from its representation, letting you build it step by step.

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 CustomerOrder {
        -String customerId
        -List~Item~ items
        -String discountCode
        -Address shippingAddress
    }
    class CustomerOrderBuilder {
        +customerId(String) CustomerOrderBuilder
        +addItem(Item) CustomerOrderBuilder
        +discountCode(String) CustomerOrderBuilder
        +shippingAddress(Address) CustomerOrderBuilder
        +build() CustomerOrder
    }
    CustomerOrderBuilder ..> CustomerOrder : builds




Java Example

public final class CustomerOrder {
    private final String customerId;
    private final List<Item> items;
    private final String discountCode;
    private final Address shippingAddress;

    private CustomerOrder(Builder builder) {
        this.customerId = builder.customerId;
        this.items = List.copyOf(builder.items);
        this.discountCode = builder.discountCode;
        this.shippingAddress = builder.shippingAddress;
    }

    public static Builder builder() {
        return new Builder();
    }

    public static final class Builder {
        private String customerId;
        private final List<Item> items = new ArrayList<>();
        private String discountCode;
        private Address shippingAddress;

        public Builder customerId(String customerId) {
            this.customerId = customerId;
            return this;
        }

        public Builder addItem(Item item) {
            this.items.add(item);
            return this;
        }

        public Builder discountCode(String discountCode) {
            this.discountCode = discountCode;
            return this;
        }

        public Builder shippingAddress(Address address) {
            this.shippingAddress = address;
            return this;
        }

        public CustomerOrder build() {
            Objects.requireNonNull(customerId, "customerId is required");
            return new CustomerOrder(this);
        }
    }
}

Usage:

CustomerOrder order = CustomerOrder.builder()
        .customerId("CUST-001")
        .addItem(new Item("SKU-1", 2))
        .discountCode("WELCOME10")
        .shippingAddress(new Address("CDMX", "MX"))
        .build();

Quarkus Example

Builders pair naturally with Quarkus/RESTEasy Reactive DTOs and with building TMF API payloads, where TMF resources (like TMF622 Product Orders) have many optional fields:

@Path("/productOrder")
public class ProductOrderResource {

    @POST
    public ProductOrder create(ProductOrderRequest request) {
        ProductOrder order = ProductOrder.builder()
                .externalId(request.externalId())
                .priority(request.priority() != null ? request.priority() : "4")
                .relatedParty(mapParty(request))
                .build();

        return orderService.persist(order);
    }
}

Quarkus doesn’t add a Builder-specific feature — the pattern stays plain Java — but it’s frequently used to assemble the record/DTO objects that flow through REST endpoints and MapStruct mappers.

Key Takeaways

  • Use Builder when an object has many optional parameters or must be built in multiple steps.
  • Making the target class immutable (final fields, no public constructor) plus a builder gives you thread-safety and validation at build() time.
  • Very common in DTO/entity construction for TMF-style APIs where many fields are optional per use case.