2026-05-07
The Proxy Pattern places a stand-in object between a client and the real object. The proxy implements the same interface, so callers can't tell the difference, but it intercepts calls to add behavior: lazy loading, access control, caching, logging, or remote communication. The real object stays focused on its actual job.
Four common flavors you'll encounter:
Real-world example: imagine a UserRepository that hits Postgres on every findById(). Wrapping it in a CachingUserRepository proxy — same interface, same method signatures — lets you add a Redis lookup in front without touching a single caller. If the cache misses, the proxy delegates to the real repository and stores the result. Tomorrow you swap in a LoggingUserRepository that wraps the caching one, and now you have audit logs too. This composition is why frameworks like Spring AOP and Hibernate generate proxies at runtime: @Transactional, @Cacheable, and lazy-loaded entity collections all work because a proxy intercepts the method call before your code runs.
Rule of thumb: if you find yourself adding "always do X before calling Y" in three or more places, a proxy collapses that duplication into one wrapper. The 3x rule applies — two callers tolerate copy-paste; three justifies the indirection.
Watch out for:
Reach for proxies when you need to add a cross-cutting concern (auth, caching, logging, lazy init) to an existing interface without forcing every caller — or the real implementation — to know about it.
