| Type | Structural |
| Purpose | Provide a surrogate or place-holder for another object to control access to it. |
Description
The Proxy pattern provides a surrogate or placeholder for another object in order to control access to it. Rather than interacting with the real subject directly, the client works through the proxy, which can decide how and when access to the real object should occur.
This is useful when direct access to an object is expensive, sensitive, or otherwise needs to be managed. For example, if data must be fetched from a server through a time-consuming operation and that data changes infrequently, a proxy can cache the results of common requests and avoid unnecessary repeated calls. Proxy can introduce additional behaviour such as caching, access control, lazy initialisation, logging, or remote access, while preserving the same interface as the real subject.
Proxy is primarily concerned with controlling access to another object.
Overview
| Subject | The interface that both the RealSubject and Proxy classes implement. It defines a common interface for both the RealSubject and the Proxy so that a Proxy can be used anywhere a RealSubject is expected. |
| RealSubject | The real object the Proxy is representing and forwarding calls to. |
| Proxy | This is the object which controls access to the RealSubject. It could be accessing it, maintaining a reference to it, managing resources, or protecting it if it's too expensive or too sensitive. |
Implementation
// Subject
public interface IImage
{
void DisplayImage();
}
// Real Subject
public class RealImage : IImage
{
private readonly string _url;
public RealImage(string url)
{
_url = url;
// Simulate an expensive operation such as an HTTP request.
Thread.Sleep(2000);
}
public void DisplayImage()
=> Console.WriteLine("Displaying image from url: " + _url);
}
// Proxy
public class ProxyImage(string url) : IImage
{
private RealImage? _realImage;
public void DisplayImage()
{
_realImage ??= new RealImage(url);
_realImage.DisplayImage();
}
}
public static class ProxyDemo
{
public static void Execute()
{
var image1 = new ProxyImage("http://example.com/image1");
var image2 = new ProxyImage("http://example.com/image2");
// image will be loaded from remote server
image1.DisplayImage();
// image will not be loaded from remote server
image1.DisplayImage();
// image will be loaded from remote server
image2.DisplayImage();
// image will not be loaded from remote server
image2.DisplayImage();
}
}
Notes
The Proxy pattern can provide more advanced features, such as persisting the cache to a local file to reduce application load times. Proxy might involve additional behaviors such as handling the interface to a remote service (like in COM+), or adding a secure interface, or perhaps adding logging capabilities:
public class ProxyImage : IImage
{
private RealImage? _realImage;
private readonly string _url;
private string _localStoragePath;
public ProxyImage(string url)
{
_url = url;
_localStoragePath = ConvertUrlToFileName(url);
}
private void FetchImage()
{
// Check if image exists locally
if (File.Exists(_localStoragePath))
{
_realImage = new RealImage(_localStoragePath);
}
else
{
// If not, fetch from the web
_realImage = new RealImage(_url);
_localStoragePath = SaveImageLocally(_url);
}
}
public void DisplayImage()
{
if (_realImage is null)
{
FetchImage();
}
_realImage!.DisplayImage();
}
private string SaveImageLocally(string url)
{
// Placeholder for downloading/storing the image locally.
return ConvertUrlToFileName(url);
}
private string ConvertUrlToFileName(string url)
{
// Replace any non-alphanumeric character with '-'
return Regex.Replace(url, "[^a-zA-Z0-9]", "-") + ".png";
}
}
Summary
For more information, please see here.