Low-level design2 min
Proxy Pattern
Type: Structural Pattern
Relevance: High. Extensively used in web frameworks, ORMs (lazy loading), and networking (gRPC stubs).
The Proxy pattern provides a surrogate or placeholder for another object to control access to it.
Real-Life Analogy
Think of a Credit Card. A credit card is a proxy for a bank account. It implements the same interface (they can both be used to make a payment). But the credit card controls access: it checks your PIN, verifies your credit limit, and saves you from carrying around a heavy suitcase full of cash (the "Real Subject").
Types of Proxies
- Virtual Proxy (Lazy Loading): Delays the creation of a massive, memory-heavy object until it is actually needed.
- Protection Proxy (Access Control): Checks permissions before allowing a request to reach the real object.
- Remote Proxy (Stubs): Represents an object located on a different server (e.g., an RPC call).
- Caching Proxy: Caches results from the real object to prevent expensive network/DB calls on subsequent requests.
How to Implement Proxy
Create a new Proxy class that implements the exact same interface as the Real Subject. Pass the proxy to the client instead of the real object. When the client calls a method on the proxy, the proxy does some work (caching, access control, lazy loading) and then forwards the request to the real object.
Example in Code (Caching / Virtual Proxy)
// 1. The Common Interface
interface ThirdPartyYouTubeLib {
getVideoInfo(id: string): string;
}
// 2. The Real Subject (Heavy/Expensive Network Calls)
class ThirdPartyYouTubeClass implements ThirdPartyYouTubeLib {
getVideoInfo(id: string): string {
console.log(`[Network Call] Downloading info for video ${id}...`);
// Imagine this takes 2 seconds over the network
return `Video Data for ${id}`;
}
}
// 3. The Proxy (Caching)
class CachedYouTubeClass implements ThirdPartyYouTubeLib {
private service: ThirdPartyYouTubeClass;
private cache: { [key: string]: string } = {};
constructor(service: ThirdPartyYouTubeClass) {
this.service = service;
}
getVideoInfo(id: string): string {
if (!this.cache[id]) {
// It only calls the expensive Real Subject if it's NOT in the cache!
this.cache[id] = this.service.getVideoInfo(id);
} else {
console.log(`[Cache Hit] Returning cached info for video ${id}`);
}
return this.cache[id];
}
}
// Client Code
const realService = new ThirdPartyYouTubeClass();
const proxy = new CachedYouTubeClass(realService);
// Client talks to the Proxy thinking it's the real service
console.log(proxy.getVideoInfo("cat_video_1")); // Triggers Network Call
console.log(proxy.getVideoInfo("cat_video_1")); // Cache Hit! (Instant)
Class Diagram
Proxy vs Decorator
- Proxy: Controls access to an object (caching, security, lazy-loading). The Proxy usually instantiates and manages the lifecycle of the real subject itself.
- Decorator: Adds behavior to an object. The Decorator is usually passed the object by the client via dependency injection.