Proxy
Structural Design Patterns
Sometimes, you need more than just a direct reference to an object. Direct access might be inefficient, insecure, or impractical. Consider these scenarios:
- A Memory-Intensive Object: Imagine a document editor that can embed huge, high-resolution images. If you load every single image into memory when the document opens, the application would be incredibly slow and consume enormous amounts of RAM. You only want to load an image when it actually becomes visible on the screen.
- A Secure Operation: You have an administrative service with a
deleteEverything()
method. You can't let just any part of the application call this method directly. You need a way to check the user's permissions before allowing the call to proceed. - A Remote Resource: The object you need to communicate with exists on a different server. Your client code can't have a direct memory reference to it; it needs an intermediary to handle the complex network communication.
In all these cases, the problem is the same: you need to put something in between the client and the real object to manage the interaction.
A Proxy for Controlled Access
The Proxy is a structural design pattern that provides a surrogate or placeholder for another object to control access to it.
The key feature of the Proxy pattern is that the Proxy object has the exact same interface as the real object it represents. This makes the proxy "transparent" to the client. The client code is written as if it's talking directly to the real object, but it's actually talking to the Proxy. The Proxy then decides if, when, and how to delegate the request to the real object (often called the "Real Subject").
The best real-world analogy is a debit card. It's a proxy for your bank account. You can use it to "pay" just like you would with cash (it has the same interface), but it adds a layer of control: it performs security checks, verifies your PIN, and checks your spending limit before allowing the transaction to go through to your actual bank funds.
The Participants
- Subject: The common interface for both the
RealSubject
and theProxy
. This is what makes them interchangeable and keeps the proxy transparent to the client. - Real Subject: The actual object that contains the core business logic. This is often a "heavy" or sensitive object that we don't want to expose directly.
- Proxy: The class that implements the same interface as the
RealSubject
. It holds a reference to theRealSubject
and can manage its lifecycle (e.g., creating it on demand). It performs its control logic before or after delegating the call to theRealSubject
.
Example: A Protection Proxy
Let's implement a "Protection Proxy" to secure access to an internet connection, banning certain sites.
Step 1: The Subject Interface
public interface IInternet {
void connectTo(String serverHost) throws Exception;
}
Step 2: The Real Subject
This class provides the core, unfiltered internet access.
public class RealInternet implements IInternet {
@Override
public void connectTo(String serverHost) {
System.out.println("Connecting to " + serverHost);
}
}
Step 3: The Proxy Class
This proxy implements the same interface but adds a layer of security checks.
import java.util.ArrayList;
import java.util.List;
public class ProxyInternet implements IInternet {
private final IInternet realInternet = new RealInternet();
private static final List<String> bannedSites;
static {
bannedSites = new ArrayList<>();
bannedSites.add("banned.com");
bannedSites.add("danger.net");
}
@Override
public void connectTo(String serverHost) throws Exception {
// This is the control logic added by the proxy.
if (bannedSites.contains(serverHost.toLowerCase())) {
throw new Exception("Access Denied to " + serverHost);
}
// If the check passes, delegate the call to the real object.
realInternet.connectTo(serverHost);
}
}
Step 4: The Client Code
The client code works with the IInternet
interface and is unaware of the proxy's existence.
public class Client {
public static void main(String[] args) {
IInternet internet = new ProxyInternet(); // The client gets the proxy object
try {
internet.connectTo("google.com");
internet.connectTo("banned.com");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
// Output:
// Connecting to google.com
// Access Denied to banned.com
Interview Focus: Analysis and Types of Proxies
Benefits:
- Control Without Modification: You can add control logic to an object's lifecycle or access without changing its source code, which is excellent for SRP and OCP.
- Transparency: Because the proxy implements the same interface as the real subject, it can be seamlessly swapped in or out without the client's knowledge.
Drawbacks:
- Increased Complexity: The pattern introduces another layer of indirection and a new class.
- Potential Latency: The logic inside the proxy adds a small amount of overhead to every call.
In an interview, it's very impressive to name and describe the common types of proxies:
- Virtual Proxy: For lazy initialization of expensive objects.
- Protection Proxy: For controlling access based on permissions, as in our example.
- Remote Proxy: A local representative for an object in a different address space (e.g., on another server). It handles all the networking complexity.
- Logging/Caching Proxy: Intercepts calls to log them or cache their results.
How Proxy Relates to Other Concepts
The Proxy pattern is fundamental for managing object interactions and is a key part of many modern frameworks.
-
The "Wrapper" Pattern Comparison:
- Proxy: Has the same interface as its subject. Its purpose is to control access.
- Decorator: Also has the same interface. Its purpose is to add responsibilities.
- Adapter: Changes the interface to make an object compatible with a client.
- Facade: Provides a new, simpler interface for an entire subsystem.
-
Real-World Examples:
- ORM Frameworks (Hibernate/JPA): Use virtual proxies extensively for lazy-loading relationships. A
User
object's list ofOrders
is often a proxy that only queries the database when the list is first accessed. - Spring AOP (Aspect-Oriented Programming): Spring's magic is largely based on dynamic proxies. It wraps your service beans in proxies to weave in cross-cutting concerns like transaction management (
@Transactional
) and security (@PreAuthorize
) before or after your methods execute. - RPC (Remote Procedure Calls): Client-side "stubs" that represent a remote service are classic examples of a Remote Proxy.
- ORM Frameworks (Hibernate/JPA): Use virtual proxies extensively for lazy-loading relationships. A