public final class HttpRequest { private final String url; private final String method; private final Map<String,String> headers; private HttpRequest(Builder b) { url=b.url; method=b.method; headers=Map.copyOf(b.headers); } public static Builder builder() { return new Builder(); } public static class Builder { private String url, method = "GET"; private final Map<String,String> headers = new HashMap<>(); public Builder url(String u) { this.url = u; return this; } public Builder header(String k, String v) { headers.put(k,v); return this; } public HttpRequest build() { Objects.requireNonNull(url, "url"); return new HttpRequest(this); } }}
5. Implementing Prototype Pattern
Step
Java Mechanism
Clone interface
Custom copy() method (avoid Cloneable)
Deep vs shallow
Recursively copy mutable fields
Registry
Map of name → prototype
6. Implementing Factory Pattern
Example: Simple factory
public final class ShapeFactory { public static Shape create(String type) { return switch (type) { case "circle" -> new Circle(); case "square" -> new Square(); default -> throw new IllegalArgumentException(type); }; }}
7. Implementing Simple Factory
Aspect
Detail
Not a GoF pattern
Idiom; central creation method
Use when
Few types, stable hierarchy
Limitation
Adding type means modifying factory (OCP cost)
8. Using Static Factory Methods
Method
Convention
of
List.of(1,2)
from
Instant.from(temporal)
valueOf
Integer.valueOf("1")
getInstance
Calendar.getInstance()
newInstance
New each call
9. Choosing Between Factory Patterns
Need
Pattern
Single type, named creation
Static factory method
Many params/optional
Builder
Subclass-decided type
Factory Method
Family of products
Abstract Factory
10. Implementing Object Pool Pattern
Use Case
Example
Expensive creation
DB connections (HikariCP)
Limited resources
Threads (ThreadPoolExecutor)
Reuse
Buffer pools
11. Implementing Registry Pattern
Example: Type registry
public class HandlerRegistry { private final Map<String, Handler> handlers = new ConcurrentHashMap<>(); public void register(String key, Handler h) { handlers.put(key, h); } public Handler resolve(String key) { return Optional.ofNullable(handlers.get(key)) .orElseThrow(() -> new NoSuchElementException(key)); }}
12. Implementing Multiton Pattern
Aspect
Detail
Goal
Bounded set of instances by key
Storage
ConcurrentHashMap<Key, Instance>
Creation
computeIfAbsent for thread safety
13. Implementing Dependency Injection Pattern
Form
Pros
Cons
Constructor
Required deps explicit, immutable
Many params
Setter
Optional deps
Mutability
Field
Concise
Hidden deps, hard to test DISCOURAGED
14. Understanding Dependency Injection
Without DI: ServiceA ──new── ServiceB (tight)
With DI: ServiceA ─uses─→ IServiceB ←─impl── ServiceB
Container injects ServiceB into ServiceA
Benefit
Detail
Testability
Inject mocks
Flexibility
Swap implementations
SRP
Class focuses on its job, not wiring
15. Using Service Locator Pattern
vs DI
Locator
DI
Discovery
Pulls from locator
Pushed in
Visibility
Hidden deps
Explicit
Testability
Harder (global)
Easier
Warning: Service Locator is often considered an anti-pattern — prefer constructor DI.
16. Understanding IoC Containers
Container
Ecosystem
Spring
Java, JVM
Guice
Java, lightweight
Dagger
Compile-time DI (Android, Java)
Micronaut/Quarkus
Compile-time, fast startup
CDI
Jakarta EE standard
17. Using Service Containers
Responsibility
Detail
Bean instantiation
Reflection or compile-time
Wiring
Resolve constructor params
Scope
Singleton, prototype, request, session
Lifecycle
init/destroy callbacks
18. Understanding Thread-Safe Singleton
Approach
Trade-off
Eager init
Simple, no lazy benefit
synchronized getter
Safe but slow
Double-checked locking
Fast; needs volatile
Holder idiom
Best balance
Enum
Simplest, serialization-safe
19. Using Double-Checked Locking
Example: Correct DCL
public class Lazy { private static volatile Lazy instance; public static Lazy get() { Lazy local = instance; if (local == null) { synchronized (Lazy.class) { local = instance; if (local == null) instance = local = new Lazy(); } } return local; }}
20. Using Initialization-on-Demand Holder
Example: Holder idiom
public class Singleton { private Singleton() {} private static class Holder { static final Singleton INSTANCE = new Singleton(); } public static Singleton get() { return Holder.INSTANCE; }}
Note: JVM guarantees class initialization is thread-safe and lazy.
21. Implementing Lazy Initialization
Tool
Use
Supplier<T>
Defer creation
Holder class
Class-level lazy
Suppliers.memoize
Guava cached supplier
22. Using Lazy<T> for Lazy Initialization
Example: Memoized supplier
public final class Lazy<T> { private final Supplier<T> init; private volatile T value; public Lazy(Supplier<T> init) { this.init = init; } public T get() { T v = value; if (v == null) synchronized (this) { if (value == null) value = v = init.get(); } return v; }}
23. Using Functional Options
Example: Configurer pattern
HttpClient client = HttpClient.create(o -> o .timeout(Duration.ofSeconds(5)) .retries(3) .followRedirects(true));
24. Using Structs for Simple Factories
Java Equivalent
Detail
Record
Concise immutable carrier
Static factory
Point.of(x,y) on the record
25. Using Class Methods as Factories
Example: Named static creators
public final class Money { public static Money usd(long cents) { return new Money(cents, USD); } public static Money zero(Currency c) { return new Money(0, c); } private Money(long cents, Currency c) { ... }}