Posts

Proxy

June 24, 2025
Proxy Pattern
The Proxy Pattern provides a stand-in or placeholder for another object. It controls access to the original object, allowing extra behavior before or after delegating to it.
What if:
  • A service object is resource-intensive (e.g., a video downloader)?
  • You want lazy loading, access control, or caching?
  • You can’t change the service class (e.g., it’s from a 3rd-party library)?
Directly modifying the service is risky or impossible. That's where a proxy steps in.
A proxy class implements the same interface as the service and manages access to it. Common purposes:
  • Lazy initialization
  • Logging
  • Caching
  • Access control
  • Remote interaction (RPC)

Credit cards act as proxies to your bank account (or cash). The shop doesn’t access your account directly—it interacts with a proxy (the card) that performs validation, authorization, etc.
  • Service Interface: Defines operations
  • Real Service: Implements core logic
  • Proxy: Implements the same interface and controls access
  • Client: Talks only to the interface

Javascript
// Service interface
class YouTubeService {
  listVideos() {}
  getVideoInfo(id) {}
  downloadVideo(id) {}
}

// Real service implementation (e.g., 3rd-party)
class RealYouTubeService extends YouTubeService {
  listVideos() {
    console.log("Fetching videos from YouTube...");
    return ["video1", "video2"];
  }

  getVideoInfo(id) {
    console.log(`Fetching info for video ${id}...`);
    return { id, title: `Video ${id}` };
  }

  downloadVideo(id) {
    console.log(`Downloading video ${id}...`);
  }
}

// Proxy with caching
class CachedYouTubeProxy extends YouTubeService {
  constructor(realService) {
    super();
    this.service = realService;
    this.videoListCache = null;
    this.videoInfoCache = {};
  }

  listVideos() {
    if (!this.videoListCache) {
      this.videoListCache = this.service.listVideos();
    }
    return this.videoListCache;
  }

  getVideoInfo(id) {
    if (!this.videoInfoCache[id]) {
      this.videoInfoCache[id] = this.service.getVideoInfo(id);
    }
    return this.videoInfoCache[id];
  }

  downloadVideo(id) {
    this.service.downloadVideo(id);
  }
}

// Client
const service = new CachedYouTubeProxy(new RealYouTubeService());
service.listVideos(); // Fetches from real YouTube
service.listVideos(); // Returns cached result
service.getVideoInfo("v1"); // Fetches and caches info
service.getVideoInfo("v1"); // Returns cached info
service.downloadVideo("v1"); // Always delegates to real service

  • Lazy loading: Defer creation of heavy objects
  • Access control: Restrict access to sensitive data
  • Caching: Avoid redundant network or computation
  • Remote access: Act as local rep for remote service (RPC)
  • Smart references: Track access or usage

✅ Pros:
  • Controls access transparently
  • Adds behavior without modifying original object
  • Supports Open/Closed Principle (can extend functionality easily)
❌ Cons:
  • More classes = more complexity
  • Potential latency due to indirect access
  • Harder to debug when proxies get deep
On this page