Overview of Design Patterns
Design patterns represent reusable, time-tested solutions to recurring software engineering challenges. By categorizing these patterns, developers achieve cleaner, more maintainable, and loosely coupled code. The Gang of Four (GoF) defined 23 classic design patterns, classified into three functional categories:
- Creational Patterns (5): Focused on object instantiation mechanisms.
- Structural Patterns (7): Concerned with class and object composition.
- Behavioral Patterns (11): Manage communication and responsibilities between objects.
Creational Patterns: Singleton
The Singleton pattern restricts a class to a single instance and provides a global access point. While several implementation strategies exist, the optimal choice depends on thread safety and performance requirements.
Thread-Safe Lazy Initialization with Double-Checked Locking
public class SingletonInstance {
private static volatile SingletonInstance instance;
private SingletonInstance() {}
public static SingletonInstance getInstance() {
if (instance == null) {
synchronized (SingletonInstance.class) {
if (instance == null) {
instance = new SingletonInstance();
}
}
}
return instance;
}
}
Structural Patterns: Adapter
The Adapter pattern converts the interface of a class into another interface that clients expect. It enables classes with incompatible interfaces to work together.
Object Adapter Example
public class SystemAdapter implements TargetInterface {
private final LegacySystem legacySystem;
public SystemAdapter(LegacySystem legacySystem) {
this.legacySystem = legacySystem;
}
@Override
public void request() {
legacySystem.legacyRequest();
}
}
Behavioral Patterns: Strategy
The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. It allows the algorithm to vary independently from the clients that use it.
Strategy Implementation
public interface SortingStrategy {
void sort(int[] data);
}
public class QuickSort implements SortingStrategy {
public void sort(int[] data) { /* Implementation */ }
}
public class SorterContext {
private SortingStrategy strategy;
public void setStrategy(SortingStrategy strategy) {
this.strategy = strategy;
}
public void executeSort(int[] data) {
strategy.sort(data);
}
}
Behavioral Patterns: Observer
The Observer pattern defines a one-to-many dependency between objects sothat when one object changes state, all its dependents are notified and updated automatically.
Observer Mechanism
public interface Observer {
void update(String message);
}
public class Subject {
private final List<Observer> observers = new ArrayList<>();
public void attach(Observer o) { observers.add(o); }
public void notifyObservers(String msg) {
for (Observer o : observers) {
o.update(msg);
}
}
}
Design patterns are not a "silver bullet"; they should be applied judiciously based on the specific architectural needs and complexity of the system.