Structural · same type, control access / lifetime / location
Proxy
A is a structural pattern that places a stand-in in front of a real object. The stand-in implements the same interface and controls when the real object is created, whether a call is allowed, or where the work actually runs.
A concrete example: a photo gallery shows fifty frames. You do not load fifty 12MB originals on page open. Each frame is an Image as far as the UI cares. Behind the frame, a stand-in loads the file the first time someone clicks display().
Timeline: page open vs first click
- T+0
Gallery mounts
Fifty LazyImage objects exist. Zero DiskImage. Heap holds frames, not originals.
- T+click
First display()
Proxy creates DiskImage(path), then forwards. Cost is paid once, on demand.
- Later
Second display()
Proxy already holds the real instance. Forward only. No second load.
Read the diagram definition
sequenceDiagram
participant UI
participant P as LazyImage
participant D as DiskImage
Note over UI,D: page open — fifty frames, no 12MB loads
UI->>P: display()
P->>D: new DiskImage(path)
D-->>P: pixels ready
P-->>UI: show
UI->>P: display() again
P->>D: display()
Note over P,D: real instance already livesCore question
Do I need a stand-in that looks like the real object but controls when, whether, or where it runs?
Keep the real type. Name the control. Forward after the check. Do not leak the real instance. The stand-in must stay .
Three kinds of control — pick one sentence
— not yet
Delay construction. DiskImage is created on first display(). Lifetime is the point.
— only if
Session must be allowed. Then forward. Whether is the point, not a log line.
— over there
Local stub, network hop, same Repository type. Location is the point.
Java sketch: virtual proxy — load on first display
interface Image {
void display();
}
class DiskImage implements Image {
DiskImage(String path) { loadFromDisk(path); }
public void display() { /* show pixels */ }
private void loadFromDisk(String path) { /* expensive */ }
}
class LazyImage implements Image {
private final String path;
private Image real;
LazyImage(String path) { this.path = path; }
public void display() {
if (real == null) real = new DiskImage(path);
real.display();
}
}Java sketch: protection proxy — same type, access check
interface Documents {
String read(String id);
}
class StoreDocuments implements Documents {
public String read(String id) { /* load body */ }
}
class AccessProxy implements Documents {
private final Documents inner;
private final Session session;
public String read(String id) {
if (!session.canView(id)) throw new SecurityException(id);
return inner.read(id);
}
}Decorator’s diagram, different intent
Both wrap. Both keep the type. Say the sentence. If you cannot name the control (lazy / ACL / remote), you may want Decorator.
| Proxy | Decorator | |
|---|---|---|
| Sentence | only if / not yet / over there | also do X (log, retry, compress) |
| Inner object | often created later by the proxy | handed in, already alive |
| Stacking | rarely nest three proxies | nest three decorators on purpose |
| Client asked for | control of access, lifetime, location | extra behavior |
| Leak test | must not hand out DiskImage | inner is already a known Notifier |
Still not Adapter or Facade
vs Adapter
Adapter changes the interface. Proxy keeps it. If LazyImage cannot be used wherever Image is used, you translated types — that is Adapter, or a leaky helper. “The type is already Image” rules out Adapter in one sentence.
vs Facade
Facade hides many classes behind one simpler method. Proxy is one-to-one with the real subject. A gallery proxy does not sequence Inventory and Mailer. See Adapter vs Facade for the shape-vs-count split; this page is the stand-in.
How to implement it (four steps)
- 01
Keep the real type
Proxy implements Image, Document, Repository — the caller does not change.
- 02
Name the control
Lazy create, deny access, remote hop, or a smart count. If you cannot name it, you may want Decorator.
- 03
Forward after the check
Load, authorize, or marshal — then call the real object. Do not reinvent its job.
- 04
Do not leak the real instance
If callers can grab DiskImage and skip the proxy, the stand-in was decoration.
Common mistakes
A logging wrapper named Proxy
Changing the interface in the stand-in
getRealImage() on the proxy
How to explain this in an interview
“This is a virtual proxy so we do not pay for DiskImage until display. It is not a decorator — we are not adding logging. It is not an adapter — the type is already Image. It is not a facade — there is one real subject, not a subsystem.”
- Gallery of 50 photos: what exists at T+0, and what is created on first click?
- AccessProxy vs LoggingNotifier: same class diagram. Write the one sentence that tells them apart.
- After Stripe is adapted to Payable, you want retry. Is that this page or Decorator?
Takeaway
Proxy is a stand-in with the same type whose job is control: not yet, only if, or over there. Also-do-X is Decorator. Wrong shape is Adapter. Too many collaborators is Facade. That sentence is what you are graded on.