The Proxy pattern implements a stand-in object that shares the same interface as a real object and controls access to it, adding behavior like lazy initialization, access control, or logging before delegating to the real object.
Key Points: • The proxy and the real subject both implement a common interface so clients treat them interchangeably. • The proxy holds a reference to the real subject, either created eagerly or lazily on first use. • Extra logic — caching, security checks, logging, remote calls — runs in the proxy before or after delegating. • Clients depend only on the shared interface, so swapping the real object for a proxy requires no client changes. • Java also supports dynamic proxies via java.lang.reflect.Proxy for interface-based proxying without hand-written classes.
Example: A caching proxy for a slow ImageLoader could implement the same Image interface, check an internal cache first, and only delegate to the real loader on a cache miss, transparently speeding up repeated requests.
Code Example:
interface Image {
void display();
}
class RealImage implements Image {
private final String fileName;
RealImage(String fileName) {
this.fileName = fileName;
loadFromDisk();
}
private void loadFromDisk() { /* expensive load */ }
public void display() { System.out.println("Displaying " + fileName); }
}
class ProxyImage implements Image {
private final String fileName;
private RealImage realImage;
ProxyImage(String fileName) {
this.fileName = fileName;
}
public void display() {
if (realImage == null) {
realImage = new RealImage(fileName);
}
realImage.display();
}
}Interview Tip: A concise interview answer is:
"I implement Proxy by defining a common interface for the real object and the proxy, having the proxy hold a reference to the real object, and adding cross-cutting logic like lazy loading, caching, or access checks in the proxy before delegating the actual call to the real subject."