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

  1. Virtual Proxy (Lazy Loading): Delays the creation of a massive, memory-heavy object until it is actually needed.
  2. Protection Proxy (Access Control): Checks permissions before allowing a request to reach the real object.
  3. Remote Proxy (Stubs): Represents an object located on a different server (e.g., an RPC call).
  4. 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)

typescript
// 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.