Design Patterns Overview
A design pattern is a reusable, named solution to a problem that recurs across many different programs. Patterns aren’t libraries or code you copy-paste verbatim — they’re a shape of solution, described abstractly enough to apply in many contexts, that developers give a shared name to. When one engineer says “let’s make this a Singleton” or “this smells like it needs a Strategy,” every other engineer who knows the pattern immediately understands the intended structure — design patterns are as much a shared vocabulary as a set of techniques.
Three classic categories
The patterns catalogued in the classic Design Patterns book (the “Gang of Four”) are grouped into three categories, based on what kind of problem they address.
Category | Concerned with | Examples |
|---|---|---|
Creational | How objects get created, hiding the concrete class or construction logic from the caller | Singleton, Builder, Factory Method, Abstract Factory |
Structural | How classes and objects are composed into larger structures | Adapter, Decorator, Composite, Facade, Proxy |
Behavioral | How objects communicate and distribute responsibility for an algorithm or workflow | Strategy, Observer, Command, Template Method, Iterator |
Creational example: Singleton
The Singleton pattern ensures a class has exactly one instance for the entire application, and provides a single, well-known way to access it — useful for things like a shared configuration object or a connection pool where having two independent instances would be a bug, not a feature.
A thread-safe Singleton using an enum
public enum AppConfig {
INSTANCE;
private final Map<String, String> settings = new HashMap<>();
public String get(String key) {
return settings.get(key);
}
public void set(String key, String value) {
settings.put(key, value);
}
}
// Usage — always the same instance, everywhere:
AppConfig.INSTANCE.set("env", "production");
String env = AppConfig.INSTANCE.get("env");An enum with a single constant is a common, safe way to implement Singleton in Java — the JVM guarantees only one instance of an enum constant is ever created, even under concurrent class loading, without needing hand-written synchronization.
Creational example: Builder
The Builder pattern separates the construction of a complex object (one with many optional fields, or where the order of construction matters) from its final representation, using a fluent chain of method calls instead of a constructor with a long, error-prone parameter list.
A fluent Builder for an object with many optional fields
public class Pizza {
private final String size;
private final boolean cheese;
private final boolean pepperoni;
private Pizza(Builder b) {
this.size = b.size;
this.cheese = b.cheese;
this.pepperoni = b.pepperoni;
}
public static class Builder {
private String size = "medium";
private boolean cheese = false;
private boolean pepperoni = false;
public Builder size(String size) { this.size = size; return this; }
public Builder cheese(boolean v) { this.cheese = v; return this; }
public Builder pepperoni(boolean v) { this.pepperoni = v; return this; }
public Pizza build() { return new Pizza(this); }
}
}
// Usage — reads like a description of the object being built:
Pizza pizza = new Pizza.Builder()
.size("large")
.cheese(true)
.pepperoni(true)
.build();Java’s own standard library uses Builder-like fluent chains widely — StringBuilder, Stream pipelines, and HttpRequest.newBuilder() in the java.net.http package all follow this shape.
A design pattern is a named, reusable solution shape for a recurring problem — shared vocabulary, not a library to import.
Creational patterns manage object creation; Structural patterns manage composition; Behavioral patterns manage communication and responsibility.
Singleton (one instance, globally accessible) and Builder (fluent, step-by-step construction) are two of the most common creational patterns in everyday Java code.
Apply a pattern because you recognize its problem in your code — not simply because the pattern is well-known.