Learn Proxy Pattern – a structural design pattern that controls object access, optimizes performance with lazy loading, and protects system resources. Includes C# code examples.
No table of contents available for this article
Proxy (also known as Surrogate) is a design pattern belonging to the Structural Pattern group. The core idea of this pattern is:
Indirectly control object access through a delegated object
Provide a representative class to manage access to components of another class
Solve problems related to security, performance, validation, and more
Usage frequency: Quite high in real-world systems.
Think of Proxy as a gatekeeper for your house. Instead of allowing guests to freely enter and leave, the gatekeeper will verify identities, record entry history, and only allow authorized people to enter.
Imagine you have a problem: you need to access a large object that consumes significant system resources. You need it frequently, but not all the time. A typical example is querying a database or loading videos from disk.
A natural solution is to implement lazy initialization – only create the object when actually needed. However, problems arise:
If multiple clients want to access it, they all must run through this checking code
Result: code duplication in many places
The ideal solution would be to put this logic into the object itself. But if this class is a third-party library, you cannot modify it.
There are also other concerns such as:
Security: Check access permissions before allowing object usage
Validation: Validate data (e.g., check files before upload)
Logging: Record request history
Proxy Pattern proposes: Create a new class representing the existing service class, with the same interface. This class is called the Proxy.
When updating the application, you pass the proxy object to all clients instead of the original object. When receiving a request from a client, the proxy will:
Perform additional logic (permission checking, logging, caching,...)
Create the real service (if needed)
Delegate the actual work to the service
Key point: Because Proxy implements the same interface as the main class, it can completely replace it without the client knowing.
Ask yourself these two questions:
Is there an "expensive resource" that you only want to initialize when actually needed?
Is there a resource that needs security, availability, or validation checks that you don't want to handle on the Client side?
If the answer is "Yes", Proxy Pattern is the right solution.
The Proxy Pattern model consists of the following main components:
ServiceInterface
Defines a common interface for Service and Proxy
Ensures Proxy can be used anywhere Service is expected
Service (RealSubject)
Implements ServiceInterface
Contains the actual business logic
Proxy
Maintains a reference to Service for access when needed
Provides an interface identical to ServiceInterface
Controls access and manages the Service lifecycle
Proxy TypeFunctionRemote ProxyEncodes requests and sends them to a Service on another serverVirtual ProxyCaches information to delay heavy object initializationProtection ProxyChecks access permissions before executing requestsLogging ProxyRecords request historyCaching ProxyStores results for reuseOpen/Closed Principle: Add new proxies without changing service or clients
Improved Performance: Through lazy loading, only create objects when truly needed
Protects real objects: Prevents unauthorized external access
Reduced costs: Especially effective with many accesses to objects with high initialization costs
Increased complexity: Must add new classes to the system
Potential delays: Response from service may be delayed due to passing through proxy
ScenarioDescriptionLazy Initialization (Virtual Proxy)Heavy service object wastes resources if always activeAccess Control (Protection Proxy)Only allow specific clients to use the serviceRemote Service (Remote Proxy)Service is located on a remote serverLogging RequestsNeed to keep history of requests to serviceCaching ResultsNeed to store results and manage cache lifecycleSmart ReferenceNeed to release heavy objects when no clients are using themLet's look at a practical example: Lazy loading video from disk.
csharp
public interface IVideo
{
void Display();
}csharp
public class RealVideo : IVideo
{
private string _fileName;
public RealVideo(string fileName)
{
_fileName = fileName;
LoadFromDisk(_fileName);
}
public void Display()
{
Console.WriteLine($"Displaying {_fileName}");
}
private void LoadFromDisk(string fileName)
{
Console.WriteLine($"Loading {fileName}...");
// Simulate loading a heavy file
Thread.Sleep(2000);
}
}csharp
public class ProxyVideo : IVideo
{
private RealVideo _realVideo;
private string _fileName;
public ProxyVideo(string fileName)
{
_fileName = fileName;
}
public void Display()
{
// Lazy initialization: only create RealVideo when truly needed
if (_realVideo == null)
{
_realVideo = new RealVideo(_fileName);
}
_realVideo.Display();
}
}csharp
class Program
{
static void Main(string[] args)
{
IVideo video = new ProxyVideo("Design_Pattern.mp4");
// First time: video will be loaded from disk
Console.WriteLine("=== First call ===");
video.Display();
Console.WriteLine("\n=== Second call ===");
// Second time: video is cached, no need to reload
video.Display();
}
}
=== First call ===
Loading Design_Pattern.mp4...
Displaying Design_Pattern.mp4
=== Second call ===
Displaying Design_Pattern.mp4As you can see, the second call doesn't need to reload the video from disk – this is the power of lazy loading in Proxy Pattern.
Adapter = "Translator" (converts interfaces)
Proxy = "Gatekeeper" (controls access)
Decorator = "Decorator" (adds features)
If you found this article helpful, explore more in the Design Patterns Series to enhance your programming skills!
[1] Refactoring.Guru. https://refactoring.guru/design-patterns
[2] Design Patterns for Dummies, Steve Holzner, PhD
[3] Head First Design Patterns, Eric Freeman
[4] Gang of Four Design Patterns 4.0
[5] Dive into Design Patterns