Skip to main content

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
🔍 Three Implementation Approaches:
  • 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.jpg
Bank account protection
Input:
protectedAccount.withdraw(1500) // guest user
Output:
Access denied: Insufficient permissions for withdrawal
Data service caching
Input:
cachingProxy.getData('user:123')
Output:
First call: fetches from API, subsequent calls: returns from cache

Concepts

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