ReviseAlgo Logo

Structural Patterns

Proxy

Provide a surrogate or placeholder for another object to control access to it, lazy-load expensive resources, or add caching and logging.

Last Updated: June 26, 2026 24 min read

The Proxy Pattern is a structural design pattern that lets you provide a substitute or placeholder for another object. A proxy controls access to the original object, allowing you to perform actions either before or after the request gets through to the real service, such as lazy initialization, caching, access control, or logging.

1. Learning Objectives

  • Differentiate between Virtual, Caching, Protection, and Remote proxies.
  • Understand the structural layout: Subject interface, RealSubject, and Proxy.
  • Evaluate the memory optimization of lazy initialization (Virtual Proxy).
  • Compare the mechanics of Proxy vs. Decorator vs. Adapter patterns.
  • Construct thread-safe, role-based protection proxies in Java, Python, and C++.

2. Problem & Naive Solution

Suppose you are building a video streaming server. The application downloads video data from a third-party content library (e.g. YouTube SDK). Downloading a video is slow, consumes network bandwidth, and uses significant RAM.

The Naive Solution

In a direct connection architecture, the client service instantiates the downloader directly and calls it on every request:

This direct integration introduces several issues:

  • Redundant Network Calls: If 100 users watch the same popular video, the system downloads the identical file from YouTube 100 times, causing huge bandwidth costs.
  • Lack of Access Control: Free users can access premium videos since there is no mechanism validating permissions before invoking the download method.
  • Uncontrolled Resource Usage: The heavy downloader is initialized on application startup even if no videos are downloaded, wasting memory.

3. Issues

Direct client access prevents control. You cannot inject security logic, track usage quotas, cache expensive database results, or defer object instantiation without rewriting client modules and violating the Single Responsibility Principle.

4. Pattern Introduction & UML

The Proxy Pattern introduces a surrogate object (the VideoProxy) that implements the same interface as the real service. The client talks to the proxy, which handles authentication checks and cache lookups. If the request is authorized and cache-misses, the proxy instantiates the RealVideoDownloader (lazy loading) and forwards the call.

UML: Proxy Pattern Structure

5. Participants

  • Subject (VideoDownloader): The shared interface defining the operations for both the real object and the proxy.
  • Real Subject (RealVideoDownloader): The concrete object containing the core business logic.
  • Proxy (VideoProxy): Holds a reference to the Real Subject, controls its lifecycle, and intercepts calls to add caching or access rules.
  • Client (VideoStreamer): Interacts with the system via the Subject interface.

6. Theory (Proxy Variations & Differences)

Proxies can be configured for different runtime duties:

  • Virtual Proxy: Defers instantiation of a resource-heavy object until it is explicitly needed (Lazy Loading).
  • Protection Proxy: Authenticates clients based on credentials or roles before permitting calls to sensitive resources.
  • Caching Proxy: Retains execution results in memory to resolve duplicate requests immediately without querying backend servers.
  • Remote Proxy: Acts as a local representative for an object that lives in a different address space or microservice (RPC wrapper).

7. Syntax Explanation

Syntax construction tips for proxies:

  • Java: Uses class delegation or JDK dynamic proxies (java.lang.reflect.Proxy) to generate proxy implementations at runtime based on interface arrays.
  • Python: Leverages dynamic magic methods (__getattr__) to intercept and delegate all attribute accesses.
  • C++: Utilizes smart pointers (like std::shared_ptr) to manage ownership transitions between the proxy and real subject safely.

8. Step-by-Step Implementation

  1. Step 1: Create the Subject interface outlining the core operation contract.
  2. Step 2: Implement the heavy RealSubject implementing the interface.
  3. Step 3: Implement the Proxy class implementing the same interface.
  4. Step 4: Inside the Proxy, add logic for caching, lazy instantiation, or security validation.
  5. Step 5: Route calls from the proxy to the RealSubject reference when appropriate.

9. Complete Code (Mini Project)

10. Code Walkthrough

Let's review the interceptor mechanics:

  • Access Guard: Before forwarding execution, the proxy checks the user role map. If access is unauthorized, it short-circuits the call immediately, preventing execution overhead.
  • Deferred Instantiation: The realService is not created when the proxy is constructed. It is instanced inside the if (!realService) block on the first valid cache-miss call.
  • Result Caching: Successful downloads populate the local cache map. Subsequent queries for matching IDs bypass downstream processing entirely.

11. Execution Flow

  1. Client Call: Client calls downloadVideo() on the proxy.
  2. Access Control: Proxy verifies permissions. If invalid, returns error.
  3. Lazy Initialization: Proxy instantiates the RealSubject if it is null.
  4. Cache Check: Proxy inspects cache map. On hit, returns data.
  5. Service Delegation: Proxy calls download on the real service, caches, and returns.

12. Internal Working (JVM & Memory Lifecycle)

Using proxies affects runtime resource allocations:

  • Virtual Proxy Memory Savings: By deferring the allocation of the RealVideoService object, the JVM avoids allocating heap blocks for database pools, buffers, and arrays until required. If the client flow never requests download operations, the heavy subject is never loaded.
  • GC Caching Hazards: A caching proxy maintains references inside its internal map fields (Map cache). Because these fields are referenced by the proxy object, the cached values cannot be garbage collected. If the cache is never cleared or evicts items, it can lead to memory leaks.

13. Complexity Analysis

  • Time Complexity: $O(1)$ constant lookup overhead for cache inspections and role validations.
  • Space Complexity: $O(M)$ where $M$ is the size of the caching map holding video data buffers.

14. Best Practices

  • Utilize Cache Eviction Policies: Use Soft/Weak references (e.g. WeakHashMap) or explicit time-to-live (TTL) bounds inside caching proxies to prevent memory leak crashes.
  • Keep Interfaces Identical: Ensure the proxy implements the exact interface as the real subject to maintain clean substitution capabilities.

15. Common Mistakes

  • Memory Leaks: Failing to implement cache eviction, leading to unlimited growth of cache fields on the heap.
  • Violating Single Responsibility: Inserting core business logic (such as video rendering calculations or pricing rules) inside the proxy.

16. Framework Usage

  • Hibernate Lazy Loading: When querying entities, Hibernate returns runtime bytecode-generated proxy subclasses. The real columns are only fetched from the database when getter methods are called.
  • Spring Transactional Proxies: Spring wraps @Transactional classes inside transaction proxies. When a client calls a method, the proxy starts database transactions, delegates to the class, and commits transactions on exit.

17. Interview Discussion

Q: What is the main difference between JDK Dynamic Proxies and CGLIB in Java?
Answer: - JDK Dynamic Proxy requires the target to implement an interface. It uses reflection to dynamically generate a class implementing that interface. - CGLIB does not require interfaces. It uses bytecode manipulation (via ASM) to generate a subclass overriding the methods of the concrete real subject class at runtime.
Q: What is the difference between Proxy and Decorator?
Answer: - Proxy controls access to a resource and manages its lifecycle. The proxy often creates and manages the real subject instance. - Decorator adds behavior to an object at runtime. The client typically instantiates both the component and the decorators, wrapping them explicitly.
Q: How do you handle multi-threading safety inside a Caching Proxy?
Answer: Use concurrent maps (e.g. ConcurrentHashMap in Java), thread locks, or double-checked locking blocks during lazy initialization and cache population steps to prevent race conditions.

18. Practice Exercises

  • Easy: Write a virtual proxy in Python that lazy-loads a large log file only when a client calls printLogs().
  • Medium: Design a DatabaseQueryExecutor with a CachingProxy storing query strings and resulting records in a map.
  • Hard: Build a remote proxy mock system where the proxy serializes method call arguments into JSON, transmits them via a simulated TCP stream, and returns the deserialized output.

19. Challenge Problem

Design a Rate-Limiting API Protection Proxy. An application communicates with a high-cost database service. The proxy must guard the database: it should block unauthorized API keys (Protection), cache queries (Caching), and limit calls to a maximum of 3 requests per minute per user key (Rate Limiting). Write the solution in Java, Python, or C++ and demonstrate it handling multiple requests from different API keys.

20. Summary & Cheat Sheet

  • Proxy intercepts operations to manage lifecycle, access control, caching, or remote connections.
  • Proxy and RealSubject implement the same interface.
  • Use Virtual Proxy to delay instantiation of heavy subsystem resources.
  • Always implement cache eviction policies to avoid memory leaks.

21. Quiz

1. What is the primary intent of the Proxy design pattern?

A) To translate incompatible interfaces
B) To control access to an object by acting as a placeholder or surrogate (Correct)
C) To combine multiple objects into trees

2. Which type of proxy defers the instantiation of a heavy object until it is requested?

A) Caching Proxy
B) Protection Proxy
C) Virtual Proxy (Correct)

3. How does Proxy differ from Decorator in terms of lifecycle control?

A) Proxy usually manages the lifecycle of the real subject; Decorators are wrapped around existing instances constructed by the client (Correct)
B) Proxy requires multiple inheritance; Decorators use composition
C) Decorators run on separate threads; Proxies do not

4. What memory bug is common when implementing a Caching Proxy with static maps without eviction policies?

A) Stack Overflow
B) Heap Memory Leak (Correct)
C) Thread deadlock

5. Which java framework uses Dynamic Proxies to manage database transaction scopes?

A) Spring Framework (Correct)
B) Logback Logging
C) Google Gson

6. What type of proxy intercepts calls to check if the caller has the necessary permissions?

A) Virtual Proxy
B) Protection Proxy (Correct)
C) Remote Proxy

7. Can a client distinguish a Proxy from the Real Subject it wraps if the pattern is correctly implemented?

A) Yes, because proxies have different method signatures
B) No, since they implement the identical Subject interface (Correct)
C) Only when running on Linux platforms

8. In Hibernate, how does lazy loading leverage proxies?

A) It compiles entities into static bytecode files
B) It returns proxy shells that only execute SQL fetch queries when getter methods are called (Correct)
C) It creates database connections on every thread

9. What is a Remote Proxy?

A) A proxy that runs inside server bios
B) A local wrapper that handles network serialization to talk to an object in another address space (Correct)
C) A proxy that caches weather reports

10. Why are JDK dynamic proxies generated at runtime instead of compile time?

A) To save hard drive compilation space
B) To provide flexible interceptor injection without generating hundreds of physical class files at build time (Correct)
C) To disable virtual vtable dispatch

22. Next Lesson Preview

In the next lesson, we will explore the Bridge Pattern. We will learn how to decouple an abstraction from its implementation so that the two can vary independently!