Structural Design Patterns
The Proxy Pattern
A Proxy provides a surrogate or "placeholder" for another object to control access to it. It acts as an intermediary, potentially adding logic before or after the request hits the real object.
The Real-World Analogy: A celebrity and their agent. Fans (clients) don't call the celebrity (real object) directly; they talk to the agent (proxy). The agent can screen calls, log requests, or handle scheduling without bothers the celebrity.
public interface UserService { void save(User user); }
public class UserServiceImpl implements UserService {
public void save(User user) { /* Save to DB */ }
}
public class UserServiceProxy implements UserService {
private final UserService target;
public UserServiceProxy(UserService target) { this.target = target; }
public void save(User user) {
System.out.println("Log: Operation Start"); // Enhancement
target.save(user); // Delegation
System.out.println("Log: Operation End"); // Enhancement
}
}
- Dynamic Proxy: Spring AOP uses JDK/CGLIB proxies to inject cross-cutting concerns (like logging or transactions) at runtime without changing the source code.
The Adapter Pattern
An Adapter allows classes with incompatible interfaces to work together by converting one interface into another.
The Real-World Analogy: A power plug converter. A Chinese 3-pin plug (legacy interface) needs an adapter to plug into a European 2-pin socket (target interface).
// Target interface expected by client
public interface MediaPlayer { void play(String filename); }
// Incompatible legacy class
public class VlcPlayer { public void playVlc(String filename) { ... } }
// Adapter
public class VlcAdapter implements MediaPlayer {
private VlcPlayer vlcPlayer = new VlcPlayer();
public void play(String filename) {
vlcPlayer.playVlc(filename); // Translation logic
}
}
The Decorator Pattern
The Decorator dynamically adds responsibilities to an object. It is a flexible alternative to subclassing for extending functionality.
The Real-World Analogy: Ordering a coffee. You start with a base "Espresso" and then "decorate" it with milk, sugar, or whipped cream. Each topping adds to the cost and taste without needing a dedicated "EspressoWithMilkAndSugar" class.
// Java I/O is the classic example
InputStream is = new FileInputStream("test.txt"); // Base
InputStream bis = new BufferedInputStream(is); // Decorator: Adds Buffering
InputStream dis = new DataInputStream(bis); // Decorator: Adds Type Reading
Proxy vs. Decorator
While they look similar structurally, their intent differs:
- Proxy: Controls access to the object (Auth, Logging, Lazy Loading). Usually fixed at compile time or by the framework.
- Decorator: Focuses on adding new behaviors. Designed for the user to "stack" features dynamically.
The Facade Pattern
A Facade provides a simplified unified interface to a complex set of interfaces in a subsystem.
// Complexity: Client must talk to CPU, RAM, and Disk
cpu.freeze(); ram.load(data); cpu.execute();
// Facade: Client just calls one method
computer.start(); // Internally manages the complex subsystem orchestration
Deep Technical Insights
JDK Proxy vs. CGLIB
- JDK Dynamic Proxy: Works only for classes that implement an interface. It uses reflection to generate the proxy class in memory.
- CGLIB: Works by generating a subclass of the target class at the bytecode level (via ASM). It's faster for method calls but cannot proxy
finalclasses orfinalmethods. Spring AOP intelligently switches between the two: if your bean has an interface, it uses JDK; if not, it uses CGLIB.
Modern Structural Trends
With the rise of Functional Programming, many structural patterns are being replaced by high-order functions or "Wrappers." However, the underlying principles (Composition over Inheritance) remain the core of every clean architecture.