Skip to content
Low Level Design Mastery Logo
LowLevelDesign Mastery

Proxy Pattern

Control access to objects - provide a placeholder or surrogate for another object!

Now let’s explore the Proxy Pattern - a powerful structural design pattern that provides a placeholder or surrogate for another object to control access to it.

Imagine you’re accessing a large file on a remote server. Instead of loading the entire file immediately (which is slow and expensive), you use a proxy that loads the file only when you actually need it. The Proxy Pattern works the same way - it provides a placeholder that controls access to the real object, adding functionality like lazy loading, access control, or caching.

The Proxy Pattern provides a surrogate or placeholder for another object to control access to it. The proxy has the same interface as the real object, allowing it to be used interchangeably.

The Proxy Pattern is useful when:

  1. You want lazy loading - Load expensive objects only when needed
  2. You want access control - Control who can access the object
  3. You want caching - Cache results to avoid expensive operations
  4. You want logging/monitoring - Track access to objects
  5. You want remote access - Access objects on remote servers

What Happens If We Don’t Use Proxy Pattern?

Section titled “What Happens If We Don’t Use Proxy Pattern?”

Without the Proxy Pattern, you might:

  • Load everything upfront - Expensive objects loaded even if not used
  • No access control - Direct access to sensitive objects
  • No caching - Expensive operations repeated unnecessarily
  • Tight coupling - Clients directly depend on real objects
  • Hard to add cross-cutting concerns - Logging, security scattered everywhere

Let’s start with a super simple example that anyone can understand!

Diagram

Here’s how the Proxy Pattern works in practice - showing how proxy controls access:

sequenceDiagram
    participant Client
    participant Proxy as ImageProxy
    participant RealImage
    
    Client->>Proxy: display()
    activate Proxy
    Proxy->>Proxy: Check if image loaded
    alt Image not loaded
        Proxy->>RealImage: load_from_disk()
        activate RealImage
        RealImage->>RealImage: Expensive loading operation
        RealImage-->>Proxy: Image loaded
        deactivate RealImage
        Proxy->>Proxy: Cache reference
    end
    Proxy->>RealImage: display()
    activate RealImage
    RealImage-->>Proxy: Image displayed
    deactivate RealImage
    Proxy-->>Client: Displayed
    deactivate Proxy
    
    Note over Client,RealImage: Proxy loads image only\nwhen needed (lazy loading)!

You’re building an image viewer. Loading images from disk is expensive. Without Proxy Pattern:

Problems:

  • Loads everything upfront - All images loaded even if not used
  • Slow startup - Expensive operations happen immediately
  • High memory usage - All objects in memory at once
  • No lazy loading - Can’t defer expensive operations
classDiagram
    class Subject {
        <<interface>>
        +request() void
    }
    class RealSubject {
        +request() void
    }
    class Proxy {
        -real_subject: RealSubject
        +request() void
        -check_access() bool
        -lazy_load() void
    }
    class Client {
        +operation() void
    }
    
    Client --> Subject : uses
    Proxy --> Subject : implements
    Proxy --> RealSubject : controls access
    RealSubject --> Subject : implements
    
    note for Proxy "Controls access to RealSubject"
    note for RealSubject "Expensive object"

Real-World Software Example: Database Query Proxy

Section titled “Real-World Software Example: Database Query Proxy”

Now let’s see a realistic software example - a database system that needs to cache query results to avoid expensive database calls.

You’re building a database access layer. Database queries are expensive. Without Proxy Pattern:

Problems:

  • No caching - Same queries executed repeatedly
  • Slow performance - Every query hits the database
  • Unnecessary load - Database accessed even for cached queries

There are different types of proxies based on their purpose:

Loads expensive objects only when needed:

Use case: Lazy loading of expensive resources

Controls access to objects based on permissions:

Use case: Access control, security

Represents objects on remote servers:

Use case: Remote method invocation, RPC

Caches results to avoid expensive operations:

Use case: Caching, performance optimization


Use Proxy Pattern when:

You want lazy loading - Load expensive objects only when needed
You want access control - Control who can access the object
You want caching - Cache results to avoid expensive operations
You want logging/monitoring - Track access to objects
You want remote access - Access objects on remote servers

Don’t use Proxy Pattern when:

Simple objects - If object is simple, proxy adds unnecessary complexity
No need for control - If you don’t need lazy loading, caching, or access control
Performance critical - Proxy adds indirection (usually negligible)
Over-engineering - Don’t add complexity for simple cases


Mistake 3: Creating Real Object Unnecessarily

Section titled “Mistake 3: Creating Real Object Unnecessarily”

  1. Lazy Loading - Loads expensive objects only when needed
  2. Access Control - Controls who can access objects
  3. Caching - Caches results to avoid expensive operations
  4. Remote Access - Accesses objects on remote servers
  5. Separation of Concerns - Adds cross-cutting concerns without modifying real object
  6. Transparent - Client doesn’t know it’s using a proxy

Proxy Pattern is a structural design pattern that provides a surrogate or placeholder for another object to control access to it. The proxy has the same interface as the real object.

  • Lazy loading - Load expensive objects only when needed
  • Access control - Control who can access objects
  • Caching - Cache results to avoid expensive operations
  • Remote access - Access objects on remote servers
  • Separation of concerns - Add cross-cutting concerns
  1. Define subject interface - Common interface for proxy and real object
  2. Implement real subject - The actual object
  3. Create proxy - Implements same interface, controls access
  4. Client uses proxy - Client interacts with proxy (transparent)
  5. Proxy delegates - Proxy delegates to real object when needed
Client → Proxy → RealSubject
  • Subject - Interface for proxy and real object
  • RealSubject - The actual object
  • Proxy - Controls access to real subject
  • Client - Uses proxy (doesn’t know it’s a proxy)
class Proxy(Subject):
def __init__(self):
self._real_subject = None
def operation(self):
if self._real_subject is None:
self._real_subject = RealSubject() # Lazy load
return self._real_subject.operation()

✅ Lazy loading
✅ Access control
✅ Caching
✅ Remote access
✅ Logging/monitoring

❌ Simple objects
❌ No need for control
❌ Performance critical
❌ Over-engineering

  • Proxy Pattern = Controls access to real object
  • Proxy = Placeholder with same interface
  • RealSubject = Actual object
  • Benefit = Lazy loading, caching, access control
  • Use Case = Expensive resources, caching, security
class Proxy(Subject):
def __init__(self):
self._real_subject = None
def operation(self):
if self._real_subject is None:
self._real_subject = RealSubject()
return self._real_subject.operation()
  • Proxy Pattern controls access to real objects
  • It has the same interface as real object
  • It’s about control, not just wrapping!
  • Use it for lazy loading, caching, access control
  • Keep it transparent - client shouldn’t know it’s a proxy!

What to say:

“Proxy Pattern is a structural design pattern that provides a surrogate or placeholder for another object to control access to it. The proxy has the same interface as the real object, allowing it to be used interchangeably while adding functionality like lazy loading, caching, or access control.”

Why it matters:

  • Shows you understand the fundamental purpose
  • Demonstrates knowledge of when to use it
  • Indicates you can explain concepts clearly

Must mention:

  • Lazy loading - Load expensive objects only when needed
  • Access control - Control who can access objects
  • Caching - Cache results to avoid expensive operations
  • Remote access - Access objects on remote servers
  • Logging/monitoring - Track access to objects

Example scenario to give:

“I’d use Proxy Pattern when building an image viewer. Loading images from disk is expensive. Instead of loading all images upfront, I create image proxies that load images only when they’re actually displayed. This improves startup time and reduces memory usage.”

Must discuss:

  • Proxy: Controls access, same interface, adds functionality like lazy loading/caching
  • Decorator: Adds behavior, same interface, wraps to enhance functionality
  • Key difference: Proxy controls access, Decorator adds behavior

Example to give:

“Proxy Pattern controls access to an object and adds functionality like lazy loading or caching. Decorator Pattern adds behavior to an object dynamically. Proxy is about control and optimization, Decorator is about adding features. Proxy typically has one real object, Decorator can wrap multiple decorators.”

Must discuss:

  • Virtual Proxy: Lazy loading of expensive resources
  • Protection Proxy: Access control and security
  • Remote Proxy: Remote object access
  • Caching Proxy: Caching expensive operations

Example to give:

“I use Virtual Proxy for lazy loading images, Protection Proxy for access control in a secure system, Remote Proxy for accessing objects on remote servers, and Caching Proxy for caching database query results. Each variant serves a specific purpose.”

Benefits to mention:

  • Lazy loading - Loads expensive objects only when needed
  • Access control - Controls who can access objects
  • Caching - Caches results to avoid expensive operations
  • Separation of concerns - Adds cross-cutting concerns without modifying real object
  • Transparent - Client doesn’t know it’s using a proxy

Trade-offs to acknowledge:

  • Complexity - Adds abstraction layer
  • Performance - Small overhead (usually negligible)
  • Over-engineering risk - Can be overkill for simple cases

Q: “What’s the difference between Proxy Pattern and Decorator Pattern?”

A:

“Proxy Pattern controls access to an object and adds functionality like lazy loading, caching, or access control. Decorator Pattern adds behavior to an object dynamically. Proxy is about control and optimization, Decorator is about adding features. Proxy typically manages one real object, Decorator can wrap multiple decorators in a chain.”

Q: “When would you use Virtual Proxy vs Caching Proxy?”

A:

“I use Virtual Proxy when I want to defer the creation of expensive objects until they’re actually needed - like loading images only when displayed. I use Caching Proxy when I want to cache the results of expensive operations - like caching database query results to avoid repeated queries.”

Q: “How does Proxy Pattern relate to SOLID principles?”

A:

“Proxy Pattern supports the Single Responsibility Principle by separating access control, caching, or lazy loading into the proxy. It supports the Open/Closed Principle - you can add functionality (proxy) without modifying the real object. It supports the Dependency Inversion Principle by having clients depend on the Subject interface rather than concrete implementations.”

Before your interview, make sure you can:

  • Define Proxy Pattern clearly in one sentence
  • Explain when to use it (with examples showing lazy loading/caching)
  • Describe Proxy vs Decorator Pattern
  • Implement Proxy Pattern from scratch
  • Compare with other structural patterns (Decorator, Adapter)
  • List proxy variants (Virtual, Protection, Remote, Caching)
  • List benefits and trade-offs
  • Identify common mistakes (not delegating, different interface)
  • Give 2-3 real-world examples
  • Connect to SOLID principles
  • Discuss when NOT to use it

Remember: Proxy Pattern is about controlling access to objects - providing a placeholder that adds functionality like lazy loading, caching, or access control! 🎯