Skip to main content
Create your own
Lesson illustration

Controlling Object Access with the Proxy Pattern

Hello! Let's dive into our next structural design pattern.

In our last lesson, we explored the Facade pattern, which provides a simplified, unified interface to a complex subsystem. This is about hiding complexity. Today, we'll look at a pattern that also acts as an intermediary, but for a different reason: control.

Your learning outcome for this lesson is to apply the Proxy pattern to control access to an object. While a Facade presents a different and simpler interface, a Proxy provides the same interface as the object it represents, allowing it to be used as a stand-in or surrogate. This transparent substitution is what enables it to control access without the client's knowledge.

What is the Proxy Pattern?

At its core, the Proxy pattern provides a placeholder for another object to control access to it. Think of a real-world proxy, like a corporate network proxy. Your request to a website doesn't go directly to the internet; it goes to the proxy server first. The proxy can then perform actions like checking if the site is blocked, logging the request, or retrieving the page from a cache before forwarding the response to you. The design pattern works on the same principle.

Let's watch a brief introduction that uses this very analogy.

The Proxy Pattern Explained and Implemented in Java | Structural Design Patterns | Geekific

This clip from Geekific clearly explains the concept of a proxy using the network proxy analogy, which is a great starting point.

Watch the first minute of the video (from 00:03 to 01:03). Notice the key functions mentioned: acting as a firewall, filtering, and caching.

Another great analogy is using a credit card. The card is a proxy for your bank account. You don't hand over your entire bank account to a merchant; you use the card, which controls access, performs validation, and completes the transaction on your behalf.

The video below explains this and the concept of an ATM as a proxy for a bank.

Proxy Design Pattern in detail | Interview Question

This video from Daily Code Buffer provides more intuitive, real-world examples to solidify your understanding of what a proxy does.

Watch from 00:20 to 03:15. Pay attention to how the credit card and ATM examples both involve an intermediary object that provides controlled access to a real, underlying resource (cash/bank account).

The Structure of the Proxy Pattern

The pattern is defined by a few key components that work together to achieve this controlled access.

Proxy Design Pattern UML Diagram
This diagram shows the fundamental structure. The Client interacts with an object through a common ServiceInterface. It doesn't know whether it's talking to the real Service or the Proxy, as both implement the same interface. The Proxy can perform actions (like `checkAccess()`) before delegating the call to the real service.

The main participants are:

  1. Subject (or Service Interface): An interface that both the RealSubject and the Proxy implement. This allows the client to treat the proxy just like the real object.
  2. RealSubject (or Service): The actual object that has the business logic. This is often a "heavy" object that is resource-intensive to create or use (e.g., a database connection, a large file).
  3. Proxy: This class implements the same Subject interface. It holds a reference to the RealSubject and can add its own logic before or after delegating the call to the real object.
  4. Client: The class that uses the object. The client interacts with the Subject interface and is unaware that it might be communicating with a proxy.

To see these components defined in more detail, let's turn to a well-structured article.

Proxy Design Pattern

This article from algomaster.io provides a clear definition and a class diagram for the Proxy pattern. It also categorizes the different types of proxies, which we will explore next.

Please read the section titled '2. What is the Proxy Pattern'. Focus on the descriptions of the Subject, RealSubject, Proxy, and Client components. Also, note the list of different proxy types like Virtual, Protection, and Remote.

Common Use Cases and Types of Proxies

The "control" that a proxy provides can manifest in several ways, leading to different categories of the pattern. Let's look at the most common ones.

1. Virtual Proxy (Lazy Initialization)

This is one of the most frequent uses of the Proxy pattern. A Virtual Proxy defers the creation and initialization of an expensive object until it is actually needed. This is a powerful performance optimization technique.

Imagine an image gallery application. Loading dozens of high-resolution images into memory on startup would be incredibly slow and wasteful. A better approach is to load a lightweight proxy for each image, and only when the user clicks to view a specific image does the proxy load the real, high-resolution image from disk.

The algomaster.io article we just looked at provides a fantastic, complete Java implementation of this exact scenario.

Proxy Design Pattern

Let's study a practical implementation of a Virtual Proxy. This example clearly shows the 'before' (eager loading) and 'after' (lazy loading with a proxy) states.

First, read section '1. The Problem: Eager Loading' to understand the performance issue with the naive approach. Then, carefully study section '3. Implementing Proxy'. Pay close attention to the ImageProxy class. Notice how the realImage field is null initially and is only created inside the display() method. This is the essence of lazy loading.

2. Protection Proxy

A Protection Proxy controls access to an object based on permissions or security rules. The proxy checks the caller's credentials before forwarding the request to the real object.

For example, in a banking application, you might have an Account object. A Protection Proxy could wrap this object to ensure that only the account owner can call sensitive methods like withdraw(), while allowing anyone to call getAccountNumber().

UML Diagram of Proxy Design Pattern (Banking Example)
This UML diagram illustrates a Protection Proxy. The `SecureBankAccountProxy` implements the same `BankAccount` interface as the `RealBankAccount`. It adds an authentication layer, controlling access to the actual bank account object.

The algomaster.io article also demonstrates how to extend the ImageProxy to act as a Protection Proxy, restricting access to certain images based on a user's role.

Proxy Design Pattern

Let's see how easy it is to add access control logic to our existing proxy.

Read the sub-section '1. Adding a Protection Proxy' within the 'Extending with Other Proxy Types' section. Note how the display() method is modified to include an access check before delegating to the real object.

Other Common Proxies

  • Remote Proxy: Represents an object that lives in a different address space (e.g., on a remote server). The proxy handles the network communication, making the remote object appear as if it were local. This is fundamental to distributed systems and technologies like RMI (Remote Method Invocation).
  • Caching Proxy: Stores the results of expensive operations and returns the cached result for subsequent, identical requests. This avoids re-computing or re-fetching data unnecessarily. The Geekific video demonstrates this with a video downloader example (from 02:26 to 03:38).
Test your understanding!

You are designing a data analytics service. You have a ReportGenerator class that connects to a data warehouse and runs a complex, time-consuming query to generate a report. Generating the same report for the same date range always produces the identical result.

If multiple users request the exact same report within a short period, you want to avoid running the expensive query every single time. Which type of proxy would you use, and how would it work?

Show answer

You would use a Caching Proxy.

  1. Structure: You would create a ReportGeneratorProxy that implements the same interface as the real ReportGenerator.
  2. Mechanism: The proxy would contain a cache, like a Map, to store previously generated reports. The map's key could be a combination of the report parameters (like the date range).
  3. Workflow: When a client requests a report, the proxy first checks its internal cache.
    • If a report for the given parameters exists in the cache (a "cache hit"), the proxy immediately returns the cached report without contacting the real ReportGenerator.
    • If the report is not in the cache (a "cache miss"), the proxy will delegate the call to the real ReportGenerator, receive the generated report, store it in the cache for future requests, and then return it to the client.

Advanced Topic: Dynamic Proxies in Java

For someone with your Java experience, it's essential to know that you don't always have to write proxy classes by hand. Java provides a powerful mechanism called Dynamic Proxy via the java.lang.reflect.Proxy class and the InvocationHandler interface.

A dynamic proxy is a class that is created at runtime. Instead of manually creating ImageProxy, ReportGeneratorProxy, etc., you can write a single, generic InvocationHandler that contains the proxy logic (like access control or logging). You then ask Java's Proxy class to create an object that implements your desired interface(s) and forwards all method calls to your handler.

This is a very powerful technique used extensively in modern frameworks like Spring (for AOP, transactions) and Hibernate.

The following article gives a fantastic, complete walkthrough of implementing a Protection Proxy for a bank account using Java's dynamic proxy feature.

Proxy Pattern

This article from dev.to provides an excellent deep dive into dynamic proxies, a topic highly relevant for professional Java developers.

Please read the sections 'Dynamic Proxy' and 'Solution'. Pay close attention to: The role of InvocationHandler. Notice there are two handlers: HolderInvocationHandler and NonHolderInvocationHandler. The invoke method within the handler, which is where the logic to intercept method calls resides. The static method Proxy.newProxyInstance(), which is used to create the proxy object at runtime.

Conclusion

The Proxy pattern is a versatile tool for managing object access. By placing a surrogate object between the client and the real subject, you gain a powerful control point.

Key Takeaways:

  • Purpose: To provide a surrogate or placeholder for another object to control access to it.
  • Structure: The Proxy and the RealSubject implement a common interface, making the proxy interchangeable from the client's perspective.
  • Key Use Cases:
    • Virtual Proxy: Lazy loading of expensive resources.
    • Protection Proxy: Implementing security and access control rules.
    • Caching Proxy: Storing results of expensive operations.
    • Remote Proxy: Hiding network complexity when dealing with remote objects.
  • Java Implementation: Proxies can be implemented manually or, more powerfully, by using Java's built-in Dynamic Proxy mechanism.

In our next lesson, we will examine the Composite pattern. This pattern allows you to compose objects into tree structures to represent part-whole hierarchies. It lets clients treat individual objects and compositions of objects uniformly, which is very useful for building hierarchical UIs, file systems, or organizational structures.

Can't find a good explanation? Sign up and we'll make it for you

Sign up