Overview
The Proxy pattern provides a stand-in object with the same interface as a real object to control access to it. The client talks to the proxy as if it were the real thing; the proxy decides when and how to forward calls. Common kinds are virtual proxies (lazy loading), protection proxies (authorization), remote proxies (network calls), and caching proxies.
Structurally identical to Decorator, the difference is intent: a proxy controls access to the subject (whether, when, where it is called), while a decorator adds new behavior. Frameworks use proxies heavily: ORM lazy loading, Spring's @Transactional, and RPC client stubs.
Visitors ask for the CEO; the receptionist checks appointments, may take a message, and only then lets someone through. Visitors interact with the receptionist as the CEO's point of contact.
When to use it
- Expensive objects should load lazily.
- Access must be checked (permissions, quotas).
- The real object lives elsewhere (remote service).
- Results can be cached transparently.
Where it shows up in interviews
Recognize it when: only authorized users can call certain methods.
- Design a document service with permissions
- Design an internet access proxy
Recognize it when: expensive resources loaded on demand.
- Design an image viewer
- Design an ORM lazy relation
Where it is used in real software
Entity relations are proxies that query the database only when accessed.
@Transactional and @Cacheable work by wrapping beans in proxies.
ES Proxy intercepts property access; gRPC clients are remote proxies for server methods.
Key terms
- Subject
- Interface shared by proxy and real object.
- Real subject
- The object doing the actual work.
- Virtual proxy
- Creates the real object on first use.
- Protection proxy
- Checks permissions before forwarding.
- Remote proxy
- Represents an object in another process.
How it works, step by step
- 1Define the subject interface
ImageLoader, DocumentService.
- 2Implement the real subject
Does the work.
- 3Implement the proxy
Same interface; holds or creates the real subject.
- 4Add control logic
Lazy creation, auth checks, caching, remoting.
- 5Give clients the proxy
They cannot tell the difference.
Kinds of proxies
Same interface, different control
| Proxy type | Controls | Example |
|---|---|---|
| Virtual | When the object is created | Load a high-res image only when displayed |
| Protection | Who can call | Only owners can delete a document |
| Remote | Where it runs | gRPC stub calling a server |
| Caching | Whether to call at all | Return cached exchange rates |
NOWProxy type: Virtual | Controls: When the object is created | Example: Load a high-res image only when displayed
All kinds share the same interface as the real subject, so clients stay unchanged.
Implementation
interface DocumentService { read(docId: string, user: User): Promise<string>; delete(docId: string, user: User): Promise<void>;} type User = { id: string; roles: string[] }; class RealDocumentService implements DocumentService { async read(docId: string) { return `content of ${docId}`; } async delete(docId: string) { console.log("deleted", docId); }} class ProtectedDocumentService implements DocumentService { constructor(private real: DocumentService, private owners: Map<string, string>) {} async read(docId: string, user: User) { return this.real.read(docId, user); // everyone can read } async delete(docId: string, user: User) { const isOwner = this.owners.get(docId) === user.id; if (!isOwner && !user.roles.includes("admin")) throw new Error("Forbidden"); return this.real.delete(docId, user); }}Complexity and performance
Tiny for local proxies.
Hidden latency.
Trade-offs
Hiding network calls or lazy loading behind a normal interface can cause unexpected latency (N+1 queries).
Another layer to debug; framework-generated proxies can confuse stack traces.
Variants and related techniques
java.lang.reflect.Proxy and ES Proxy generate proxies at runtime.
Counts references or logs accesses.
Common mistakes
- Lazy proxies causing N+1 queries.
Fix: Fetch eagerly when you know data will be needed.
- Self-invocation bypassing Spring proxies.
Fix: Calls within the same class skip the proxy; move the method or call through the bean.
Interview questions
Proxy vs Decorator?
Same structure. Proxy controls access to the subject (lazy creation, permissions, remoting, caching); Decorator adds new responsibilities to it. Proxies usually manage the subject's lifecycle; decorators are given the wrapped object.
How does Spring's @Transactional work?
Spring wraps the bean in a proxy that begins a transaction before delegating to the real method and commits or rolls back afterward.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Lazy-loading image gallery | Easy | Virtual proxy. |
| Rate-limited API client proxy | Medium | Access control. |