Proxy Pattern
Control access to objects through placeholder intermediaries
Pattern Overview
🔗 The Proxy Pattern - Access Control
Provides a placeholder or surrogate for another object to control access to it. Perfect for adding functionality without changing the original!
- Core Problem Solved:
- Control access to expensive or sensitive objects
- Add functionality without modifying original objects
- Implement lazy loading and resource management
- Provide transparent access control and monitoring
- Virtual Proxy: Defer expensive operations until needed
- Protection Proxy: Control access based on permissions
- Smart Proxy: Add extra functionality like caching or monitoring
- Real-World Applications:
- CDN networks as proxies for web content delivery
- ORM lazy loading for database relationships
- API rate limiting and throttling mechanisms
- Image placeholders that load on demand
- Security proxies for access control
- Caching layers for expensive operations
- Modern Usage Examples:
- JavaScript Proxy for object interception
- React lazy loading components
- Service workers as network proxies
- Database connection pooling proxies
Examples:
Image lazy loading
Input:
imageProxy.display()Output:
Proxy: Creating real image (lazy loading) -> Displaying image: photo.jpgBank account protection
Input:
protectedAccount.withdraw(1500) // guest userOutput:
Access denied: Insufficient permissions for withdrawalData service caching
Input:
cachingProxy.getData('user:123')Output:
First call: fetches from API, subsequent calls: returns from cacheConcepts
Access ControlLazy LoadingObject SurrogatesTransparent IntermediationResource Management
Complexity Analysis
Time:O(1)
Space:O(1)
Implementation
virtual-proxy
Time: O(1) | Space: O(1)
// Subject interface
interface Image {
display(): void;
getSize(): number;
}
// Expensive real subject
class RealImage implements Image {
private filename: string;
private data: Buffer | null = null;
constructor(filename: string) {
this.filename = filename;
this.loadFromDisk(); // Expensive operation!
}
private loadFromDisk(): void {
console.log(`Loading image from disk: ${this.filename}`);
this.data = Buffer.from(`Image data for ${this.filename}`);
}
display(): void {
console.log(`Displaying image: ${this.filename}`);
}
getSize(): number {
return this.data?.length || 0;
}
}
// Virtual proxy delays creation until needed
class ImageProxy implements Image {
private realImage: RealImage | null = null;
private filename: string;
constructor(filename: string) {
this.filename = filename;
// No expensive loading here!
}
display(): void {
// Lazy loading - create real image only when needed
if (!this.realImage) {
console.log('Proxy: Creating real image (lazy loading)');
this.realImage = new RealImage(this.filename);
}
this.realImage.display();
}
getSize(): number {
if (!this.realImage) {
console.log('Proxy: Returning cached size without loading');
return 0; // Could return cached metadata
}
return this.realImage.getSize();
}
}
// Usage
const image = new ImageProxy('large-photo.jpg');
// No loading happens here!
console.log(image.getSize()); // No image loaded
image.display(); // Now the real image is loaded and displayed