Level 2 · 25 min
Creational Patterns
Creational patterns abstract the instantiation process. They decouple clients from the specific classes they need to instantiate, making systems more flexible and testable.
Singleton and Factory Method
Singleton ensures only one instance of a class exists and provides global access. Thread-safety requires double-checked locking or eager initialization. Singleton makes testing harder (global mutable state). Factory Method defines an interface for creating an object but lets subclasses decide which class to instantiate — the creator method is a hook for subclasses.
Abstract Factory and Builder
Abstract Factory creates families of related objects without specifying concrete classes — ideal for cross-platform UI: WindowsFactory creates WindowsButton + WindowsDialog; MacFactory creates MacButton + MacDialog. Builder separates construction from representation — allows step-by-step construction of complex objects. Useful when constructors have many parameters (telescoping constructor anti-pattern). GoF defines Builder's intent precisely: "Separate the construction of a complex object from its representation so that the same construction process can create different representations." (Gamma et al., Design Patterns, p.97). The canonical example from GoF is an RTF document reader that uses different TextConverter builders (ASCIIConverter, TeXConverter, TextWidgetConverter) — the RTFReader director drives the same parsing algorithm while each builder produces a different output representation. In Java, this maps directly to javax.xml.parsers.DocumentBuilder, where the DocumentBuilderFactory creates builders configured for namespace-awareness, validation, and other options — clients call the same build steps regardless of the underlying XML implementation.
Prototype
Prototype creates new objects by cloning an existing instance. Useful when object creation is expensive (DB query, complex initialization) and you need many similar objects. Java: implement Cloneable and override clone(). Deep copy vs shallow copy is a critical implementation concern — shallow copy shares references to mutable objects.
Code example
// Builder pattern
public class HttpRequest {
private final String url;
private final String method;
private final Map<String, String> headers;
private final String body;
private final int timeoutMs;
private HttpRequest(Builder b) {
this.url = b.url; this.method = b.method;
this.headers = b.headers; this.body = b.body;
this.timeoutMs = b.timeoutMs;
}
public static class Builder {
private final String url;
private String method = "GET";
private Map<String, String> headers = new HashMap<>();
private String body;
private int timeoutMs = 5000;
public Builder(String url) { this.url = url; }
public Builder method(String m) { this.method = m; return this; }
public Builder header(String k, String v) { this.headers.put(k, v); return this; }
public Builder body(String b) { this.body = b; return this; }
public Builder timeout(int ms) { this.timeoutMs = ms; return this; }
public HttpRequest build() { return new HttpRequest(this); }
}
}