Proxy is the right wrapper when you need to control access to an object rather than change its interface or simplify a subsystem.
Provide a surrogate or placeholder for another object in order to control access to it.
What the pattern is trying to do
Provide a surrogate or placeholder for another object in order to control access to it.
What force creates the need
A client should see the same interface, but calls need mediation for access control, lazy loading, remote invocation, or caching.
How the pattern responds
Insert a proxy that implements the same interface and adds access logic around delegation to the real subject.
How the moving parts fit together
Client and real subject share an interface, while the proxy stands in front and controls when or how the real object is reached.
Java example in the Order Management domain
package org.javaomnibus.ecommerce.gof.structural;
public interface CustomerProfileRepository {
CustomerProfile findById(String customerId);
}
public final class SecureCustomerProfileRepositoryProxy implements CustomerProfileRepository {
private final CustomerProfileRepository target;
private final AccessPolicy accessPolicy;
public SecureCustomerProfileRepositoryProxy(
CustomerProfileRepository target,
AccessPolicy accessPolicy
) {
this.target = target;
this.accessPolicy = accessPolicy;
}
@Override
public CustomerProfile findById(String customerId) {
accessPolicy.requirePermission("customer-profile:read");
return target.findById(customerId);
}
}
Step by step
- The client still depends on CustomerProfileRepository.
- The proxy adds access checks before delegation.
- The interface stays the same, which distinguishes Proxy from Adapter.
- Proxy is especially clear when the force is access, remoting, lazy loading, or caching.
Where this pattern helps
- When access control, caching, remote access, or lazy loading should be added transparently
- When clients should keep the same interface while object access is mediated
When a simpler design is better
- When the problem is subsystem simplification or interface mismatch instead
- When a direct collaborator with explicit logic would be clearer than a transparent stand-in
What this pattern costs
- Transparent mediation can hide important performance or remoting costs
- Too many proxies can make behavior and debugging paths harder to reason about
How this fits today
Proxy remains common in Java through security wrappers, remote stubs, virtual proxies, ORM lazy loading, and framework-generated dynamic proxies.
Where teams get it wrong
Calling any wrapper a proxy blurs an important distinction; if the wrapper changes the interface or simplifies a subsystem, it is solving a different problem.