Sync Factory Method, Abstract Factory, Builder, Prototype to Java 25; fix post/code mismatches
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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); }
|
||||
}
|
||||
}
|
||||
27
01-creational/builder/HttpClientConfig.java
Normal file
27
01-creational/builder/HttpClientConfig.java
Normal 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();
|
||||
}
|
||||
}
|
||||
76
01-creational/builder/HttpRequest.java
Normal file
76
01-creational/builder/HttpRequest.java
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user