Sync Factory Method, Abstract Factory, Builder, Prototype to Java 25; fix post/code mismatches

This commit is contained in:
2026-06-23 11:00:02 +05:30
parent 2f684bf3d7
commit 2fbf89875b
51 changed files with 634 additions and 337 deletions

View File

@@ -1,12 +0,0 @@
package builder;
/** Director knows how to build common configurations */
public class Director {
public House buildStarter(House.Builder builder) {
return builder.rooms(2).floors(1).garage(false).roofType("gabled").build();
}
public House buildLuxury(House.Builder builder) {
return builder.rooms(6).floors(3).garage(true).garden(true).pool(true).roofType("hip").build();
}
}

View File

@@ -1,43 +0,0 @@
package builder;
public class House {
private final int rooms;
private final int floors;
private final boolean hasGarage;
private final boolean hasGarden;
private final boolean hasPool;
private final String roofType;
private House(Builder builder) {
this.rooms = builder.rooms;
this.floors = builder.floors;
this.hasGarage = builder.hasGarage;
this.hasGarden = builder.hasGarden;
this.hasPool = builder.hasPool;
this.roofType = builder.roofType;
}
@Override
public String toString() {
return "House{rooms=" + rooms + ", floors=" + floors
+ ", garage=" + hasGarage + ", garden=" + hasGarden
+ ", pool=" + hasPool + ", roof='" + roofType + "'}";
}
public static class Builder {
private int rooms = 1;
private int floors = 1;
private boolean hasGarage = false;
private boolean hasGarden = false;
private boolean hasPool = false;
private String roofType = "flat";
public Builder rooms(int rooms) { this.rooms = rooms; return this; }
public Builder floors(int floors) { this.floors = floors; return this; }
public Builder garage(boolean v) { this.hasGarage = v; return this; }
public Builder garden(boolean v) { this.hasGarden = v; return this; }
public Builder pool(boolean v) { this.hasPool = v; return this; }
public Builder roofType(String roofType) { this.roofType = roofType; return this; }
public House build() { return new House(this); }
}
}

View File

@@ -0,0 +1,27 @@
package builder;
public class HttpClientConfig {
// Director method 1: a health check probe — short timeout, no retries
public HttpRequest buildHealthCheck(String baseUrl) {
return new HttpRequest.Builder()
.url(baseUrl + "/health")
.method("GET")
.timeoutMs(2_000) // fast fail for health checks
.maxRetries(0)
.build();
}
// Director method 2: a resilient POST with standard JSON headers and retries
public HttpRequest buildResilientPost(String url, String jsonBody) {
return new HttpRequest.Builder()
.url(url)
.method("POST")
.body(jsonBody)
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.timeoutMs(15_000)
.maxRetries(3)
.build();
}
}

View File

@@ -0,0 +1,76 @@
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<String, String> 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<String, String> 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<String, String> 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
}
}
}

View File

@@ -1,20 +1,42 @@
package builder;
public class Main {
public static void main(String[] args) {
System.out.println("=== Builder Pattern Demo ===\n");
Director director = new Director();
// Simple GET — only the required field
HttpRequest ping = new HttpRequest.Builder()
.url("https://api.example.com/health")
.build();
House starter = director.buildStarter(new House.Builder());
System.out.println("Starter home : " + starter);
System.out.println("Simple GET: " + ping);
House luxury = director.buildLuxury(new House.Builder());
System.out.println("Luxury home : " + luxury);
// Full POST with authentication and retry
HttpRequest post = new HttpRequest.Builder()
.url("https://api.example.com/orders")
.method("POST")
.body("{\"item\":\"book\",\"qty\":1}")
.header("Content-Type", "application/json")
.header("Authorization", "Bearer eyJ...")
.timeoutMs(10_000) // 10 seconds — name is self-documenting
.maxRetries(3)
.build();
// Client builds a custom house directly without the Director
House custom = new House.Builder()
.rooms(4).floors(2).garden(true).roofType("mansard").build();
System.out.println("Custom home : " + custom);
System.out.println("POST: " + post);
// build() throws if url is missing — validation at construction time
try {
HttpRequest bad = new HttpRequest.Builder().build(); // no URL!
} catch (IllegalStateException e) {
System.out.println("Caught: " + e.getMessage()); // url() is required
}
// Using the Director for the two standard presets defined above
HttpClientConfig config = new HttpClientConfig();
HttpRequest healthCheck = config.buildHealthCheck("https://api.example.com");
System.out.println("Health check: " + healthCheck);
HttpRequest order = config.buildResilientPost("https://api.example.com/orders", "{\"qty\":1}");
System.out.println("Order: " + order);
}
}