package builder; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; public final class HttpRequest { // All fields are final: the object is immutable after build() returns. private final String url; private final String method; private final String body; private final Map headers; private final int timeoutMs; private final int maxRetries; // Private: the only path to an HttpRequest is through Builder.build() private HttpRequest(Builder builder) { this.url = Objects.requireNonNull(builder.url, "URL is required"); this.method = builder.method; this.body = builder.body; this.headers = Collections.unmodifiableMap(new HashMap<>(builder.headers)); this.timeoutMs = builder.timeoutMs; this.maxRetries = builder.maxRetries; } // Getters only — no setters, enforcing immutability public String getUrl() { return url; } public String getMethod() { return method; } public String getBody() { return body; } public Map getHeaders() { return headers; } public int getTimeoutMs() { return timeoutMs; } public int getMaxRetries() { return maxRetries; } @Override public String toString() { return String.format("%s %s (timeout=%dms, retries=%d, headers=%s, body=%s)", method, url, timeoutMs, maxRetries, headers, body); } // ----------------------------------------------------------------------- // THE BUILDER — static inner class so it can call the private constructor // ----------------------------------------------------------------------- public static class Builder { // Required field — no default, must be set private String url; // Optional fields with sensible defaults private String method = "GET"; private String body = null; private Map headers = new HashMap<>(); private int timeoutMs = 30_000; // 30 seconds private int maxRetries = 0; // Fluent methods: each returns 'this' so calls can be chained public Builder url(String url) { this.url = url; return this; } public Builder method(String method) { this.method = method; return this; } public Builder body(String body) { this.body = body; return this; } public Builder header(String key, String v) { headers.put(key, v); return this; } public Builder timeoutMs(int ms) { this.timeoutMs = ms; return this; } public Builder maxRetries(int n) { this.maxRetries = n; return this; } // Terminal method: validates and creates the immutable HttpRequest public HttpRequest build() { if (url == null || url.isBlank()) { throw new IllegalStateException("url() is required before calling build()"); } if (maxRetries < 0) { throw new IllegalStateException("maxRetries must be >= 0"); } return new HttpRequest(this); // calls the private constructor } } }