Fundamental Design Patterns in Java
Design Patterns are reusable solutions to common problems. Knowing them allows you to write more elegant, maintainable code and communicate effectively with other developers.
Covered Patterns
- Singleton - Single instance
- Factory Method - Delegated creation
- Builder - Complex construction
- Strategy - Interchangeable algorithms
- Observer - Event notifications
Singleton
// Classic singleton (thread-safe with enum)
public enum DatabaseConnection {
INSTANCE;
private Connection connection;
DatabaseConnection() {
// Initialize connection
}
public Connection getConnection() {
return connection;
}
}
// Usage
DatabaseConnection.INSTANCE.getConnection();
// Singleton with double-checked locking
public class ConfigurationManager {
private static volatile ConfigurationManager instance;
private Properties config;
private ConfigurationManager() {
config = new Properties();
// Load configuration
}
public static ConfigurationManager getInstance() {
if (instance == null) {
synchronized (ConfigurationManager.class) {
if (instance == null) {
instance = new ConfigurationManager();
}
}
}
return instance;
}
public String getProperty(String key) {
return config.getProperty(key);
}
}
Factory Method
// Product interface
interface Document {
void open();
void save();
}
// Concrete products
class PdfDocument implements Document {
public void open() { System.out.println("Open PDF"); }
public void save() { System.out.println("Save PDF"); }
}
class WordDocument implements Document {
public void open() { System.out.println("Open Word"); }
public void save() { System.out.println("Save Word"); }
}
// Factory
class DocumentFactory {
public static Document create(String type) {
return switch (type.toUpperCase()) {
case "PDF" -> new PdfDocument();
case "WORD" -> new WordDocument();
default -> throw new IllegalArgumentException("Unsupported type");
};
}
}
// Usage
Document doc = DocumentFactory.create("PDF");
doc.open();
Builder
public class Student {
private final String firstName;
private final String lastName;
private final String email;
private final int age;
private final String course;
private Student(Builder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.email = builder.email;
this.age = builder.age;
this.course = builder.course;
}
// Getters...
public static class Builder {
private final String firstName; // Required
private final String lastName; // Required
private String email;
private int age;
private String course;
public Builder(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public Builder email(String email) {
this.email = email;
return this;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Builder course(String course) {
this.course = course;
return this;
}
public Student build() {
return new Student(this);
}
}
}
// Usage
Student student = new Student.Builder("John", "Doe")
.email("john@email.com")
.age(22)
.course("Computer Science")
.build();
Strategy
// Strategy interface
interface SortingStrategy {
void sort(int[] array);
}
// Concrete strategies
class BubbleSort implements SortingStrategy {
public void sort(int[] array) {
// Bubble sort implementation
System.out.println("Sorting with Bubble Sort");
}
}
class QuickSort implements SortingStrategy {
public void sort(int[] array) {
// Quick sort implementation
System.out.println("Sorting with Quick Sort");
}
}
// Context
class Sorter {
private SortingStrategy strategy;
public void setStrategy(SortingStrategy strategy) {
this.strategy = strategy;
}
public void executeSort(int[] array) {
strategy.sort(array);
}
}
// Usage
Sorter sorter = new Sorter();
sorter.setStrategy(new QuickSort());
sorter.executeSort(new int[]{5, 2, 8, 1});
Observer
import java.util.*;
// Observer interface
interface Observer {
void update(String message);
}
// Subject (Observable)
class TelegramChannel {
private List<Observer> subscribers = new ArrayList<>();
private String latestMessage;
public void subscribe(Observer o) {
subscribers.add(o);
}
public void unsubscribe(Observer o) {
subscribers.remove(o);
}
public void publishMessage(String message) {
this.latestMessage = message;
notifyAll();
}
private void notifyAllObservers() {
for (Observer o : subscribers) {
o.update(latestMessage);
}
}
}
// Concrete observer
class User implements Observer {
private String name;
public User(String name) {
this.name = name;
}
public void update(String message) {
System.out.println(name + " received: " + message);
}
}
// Usage
TelegramChannel channel = new TelegramChannel();
channel.subscribe(new User("John"));
channel.subscribe(new User("Jane"));
channel.publishMessage("New article published!");
When to Use Patterns
Selection Guide
| Pattern | When to Use |
|---|---|
| Singleton | Single global instance (config, connection pool) |
| Factory | Object creation based on parameters |
| Builder | Objects with many optional parameters |
| Strategy | Behavior that varies at runtime |
| Observer | State change notifications |







