Java Design Pattern
Last updated: Jul 28, 2026Table of Contents
Java Design Pattern
Concise reference to the most interview-relevant patterns with minimal Java snippets.
0) Categories
| Category | Purpose | Examples |
|---|---|---|
| Creational | How objects are created | Singleton, Factory, Builder |
| Structural | How objects are composed | Decorator, Adapter, Proxy |
| Behavioral | How objects interact | Strategy, Observer, Template Method |
1) Singleton (Creational)
One instance, global access. Thread-safe, lazy, and efficient version:
// Bill Pugh holder idiom — lazy, thread-safe, no locking
public class Config {
private Config() {}
private static class Holder { static final Config INSTANCE = new Config(); }
public static Config getInstance() { return Holder.INSTANCE; }
}
enum singletons are also safe against reflection & serialization:
public enum Config { INSTANCE; }
Watch out: singletons are global state — hard to test, can hide dependencies.
2) Factory Method (Creational)
Defer instantiation to a method so callers depend on an interface, not a concrete class.
interface Shape { void draw(); }
class Circle implements Shape { public void draw() {} }
class Square implements Shape { public void draw() {} }
class ShapeFactory {
static Shape create(String type) {
return switch (type) {
case "circle" -> new Circle();
case "square" -> new Square();
default -> throw new IllegalArgumentException(type);
};
}
}
3) Builder (Creational)
Construct complex/optional-heavy objects step by step; avoids telescoping constructors.
class User {
private final String name; // required
private final int age; // optional
private User(Builder b) { this.name = b.name; this.age = b.age; }
static class Builder {
private final String name;
private int age;
Builder(String name) { this.name = name; }
Builder age(int age) { this.age = age; return this; }
User build() { return new User(this); }
}
}
// usage: new User.Builder("Sam").age(30).build();
4) Strategy (Behavioral)
Encapsulate interchangeable algorithms; swap behavior at runtime.
interface SortStrategy { void sort(int[] a); }
class Sorter {
private SortStrategy strategy;
Sorter(SortStrategy s) { this.strategy = s; }
void setStrategy(SortStrategy s) { this.strategy = s; }
void run(int[] a) { strategy.sort(a); }
}
// A lambda IS a strategy: new Sorter(a -> Arrays.sort(a));
5) Observer (Behavioral)
One-to-many: subject notifies subscribers on state change (pub/sub, event listeners).
interface Observer { void update(String event); }
class Subject {
private final List<Observer> observers = new ArrayList<>();
void subscribe(Observer o) { observers.add(o); }
void notifyAll(String event) { observers.forEach(o -> o.update(event)); }
}
6) Decorator (Structural)
Add responsibilities dynamically by wrapping — an alternative to subclassing.
interface Coffee { double cost(); }
class Espresso implements Coffee { public double cost() { return 2.0; } }
class MilkDecorator implements Coffee {
private final Coffee inner;
MilkDecorator(Coffee c) { this.inner = c; }
public double cost() { return inner.cost() + 0.5; }
}
// usage: new MilkDecorator(new Espresso()) → 2.5
Java I/O (BufferedReader(new FileReader(...))) is a real-world decorator chain.
7) When to Use — Quick Map
| Need | Pattern |
|---|---|
| Exactly one shared instance | Singleton |
| Decide concrete type at runtime | Factory |
| Many optional constructor params | Builder |
| Swap an algorithm at runtime | Strategy |
| Notify many on a change | Observer |
| Add behavior without subclassing | Decorator |
DI (Dependency Injection) — supply a class’s collaborators from outside instead of constructing them internally; improves testability and decoupling.