Add all 23 GoF design pattern implementations
This commit is contained in:
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
|
||||
}
|
||||
}
|
||||
}
|
||||
42
01-creational/builder/Main.java
Normal file
42
01-creational/builder/Main.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package builder;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
// Simple GET — only the required field
|
||||
HttpRequest ping = new HttpRequest.Builder()
|
||||
.url("https://api.example.com/health")
|
||||
.build();
|
||||
|
||||
System.out.println("Simple GET: " + ping);
|
||||
|
||||
// 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();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
30
01-creational/builder/README.md
Normal file
30
01-creational/builder/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Builder Design Pattern — Java Example
|
||||
|
||||
**Pattern:** Creational → Builder
|
||||
**Article:** https://ankurm.com/builder-design-pattern-java/
|
||||
|
||||
## What this example shows
|
||||
|
||||
An immutable `HttpRequest` assembled step by step instead of through a constructor with a dozen optional parameters. `HttpRequest` is the product — built once, never mutated afterward. `HttpClientConfig` is a director-style helper that encapsulates common request configurations so callers don't repeat the same builder calls. `Main` wires it together and shows the fluent builder chain in action.
|
||||
|
||||
## How to run
|
||||
|
||||
```bash
|
||||
javac builder/*.java -d out/builder
|
||||
java -cp out/builder builder.Main
|
||||
```
|
||||
|
||||
Requires Java 25.
|
||||
|
||||
## Post Section ↔ File Mapping
|
||||
|
||||
| Post Section | File(s) |
|
||||
|---|---|
|
||||
| Part 1 — The Product: HttpRequest (Immutable) | `HttpRequest.java` |
|
||||
| Part 2 — The Director: Encapsulating Common Configurations | `HttpClientConfig.java` |
|
||||
| Part 3 — Using the Builder: Client Code | `Main.java` |
|
||||
|
||||
Note: the Lombok `@Builder` example and the JDK standard-library snippets (`HttpRequest.newBuilder()`, `StringBuilder`, `ProcessBuilder`) are illustrative only — they are not part of this repository's runnable example.
|
||||
|
||||
Article: https://ankurm.com/builder-design-pattern-java/
|
||||
All patterns: https://ankurm.com/design-patterns-java/
|
||||
Reference in New Issue
Block a user