Introduction
The Proxy Design Pattern is one of the most practical structural patterns in object-oriented programming. It’s widely used in modern frameworks to improve performance, control access, and manage object creation more efficiently. In this post, we’ll cover what the pattern is, the different types of proxies, and walk through a simple code example.
What is the Proxy Design Pattern?
A Proxy is a structural design pattern that provides a substitute or placeholder object which controls access to another object (called the “real subject”). Instead of interacting with the real object directly, the client interacts with the proxy, which then decides when and how to forward the request to the real object.
Common Types of Proxies
| Type | Purpose |
|---|---|
| Virtual Proxy | Delays creation of an expensive object until it’s actually needed (lazy loading) |
| Remote Proxy | Represents an object that exists in a different address space (e.g., a web service) |
| Protection Proxy | Controls access based on permissions |
| Caching Proxy | Caches results of expensive operations |
Why Use It?
- Performance – Avoid instantiating heavy objects until they’re actually required.
- Lazy Initialization – Defer expensive setup (DB connections, API calls, large object graphs).
- Access Control – Add a layer of validation or security before reaching the real object.
- Separation of Concerns – Keep the real object focused on business logic while the proxy handles cross-cutting concerns.
A Simple Proxy Example (PHP)
Here’s a generic example showing a Virtual Proxy that delays loading a “heavy” object until it’s actually needed:
php
<?php
// The common interface
interface ReportGeneratorInterface
{
public function generate(): string;
}
// The real, expensive-to-create object
class RealReportGenerator implements ReportGeneratorInterface
{
public function __construct()
{
// Simulate an expensive operation (DB queries, file parsing, etc.)
echo "Loading heavy resources...\n";
sleep(1);
}
public function generate(): string
{
return "Report data generated.";
}
}
// The Proxy
class ReportGeneratorProxy implements ReportGeneratorInterface
{
private ?RealReportGenerator $realGenerator = null;
public function generate(): string
{
// Object is only created when actually needed
if ($this->realGenerator === null) {
$this->realGenerator = new RealReportGenerator();
}
return $this->realGenerator->generate();
}
}
// Client code
function clientCode(ReportGeneratorInterface $generator)
{
echo "Proxy created. Real object not loaded yet.\n";
echo $generator->generate() . "\n";
}
$proxy = new ReportGeneratorProxy();
clientCode($proxy);Output:
Proxy created. Real object not loaded yet.
Loading heavy resources...
Report data generated.Notice that RealReportGenerator is only instantiated when generate() is actually called — not when the proxy itself is created. This is the essence of a Virtual Proxy.
Conclusion
The Proxy pattern is a simple yet powerful tool for controlling object creation and access. It’s the foundation for how many frameworks handle lazy loading and dependency management under the hood — including e-commerce platforms like Adobe Commerce, which we’ll explore in the next post.