Proxy Pattern
Overview
A gallery may postpone loading an image until it is actually displayed. Instead of adding a loading check to every caller, provide a substitute that can be used like an image.
Proxy accepts requests through the real object contract and controls access to it. This example creates the real object only on the first draw.
UML
RealImage and ImageProxy implement IImage. ImageProxy holds Lazy<RealImage> and delegates Draw() to the real object.
History
GoF cataloged Proxy as a structural pattern in Design Patterns in 1994. A substitute or placeholder controls access to another object.1
Why use it?
Callers use the real image and its proxy in the same way. The proxy owns the decision to defer creation.
An authorization proxy must check current permission on each request. This example covers deferred creation only; it supplies neither authentication nor remote communication.
Example
Compare the real-object count before and after the first draw. The stand-in counts construction instead of opening files.
using System;
using System.Collections.Generic;
using System.Linq;
IImage image = new ImageProxy();
Console.WriteLine(RealImage.Created);
Console.WriteLine(image.Draw());
Console.WriteLine(RealImage.Created);
interface IImage { string Draw(); }
sealed class RealImage : IImage
{
public static int Created { get; private set; }
public RealImage() => Created++;
public string Draw() => "draw image";
}
sealed class ImageProxy : IImage
{
private readonly Lazy<RealImage> image = new(() => new RealImage());
public string Draw() => image.Value.Draw();
}
0
draw image
1Constructing the proxy does not construct RealImage. The first access to Lazy.Value creates it.
An extended example
Draw twice through the same proxy and check that the real object was created only once.
using System;
using System.Collections.Generic;
using System.Linq;
IImage image = new ImageProxy();
Console.WriteLine(image.Draw());
Console.WriteLine(image.Draw());
Console.WriteLine("created: " + RealImage.Created);
interface IImage { string Draw(); }
sealed class RealImage : IImage
{
public static int Created { get; private set; }
public RealImage() => Created++;
public string Draw() => "draw image";
}
sealed class ImageProxy : IImage
{
private readonly Lazy<RealImage> image = new(() => new RealImage());
public string Draw() => image.Value.Draw();
}
draw image
draw image
created: 1Lazy reuses the object, not the result of Draw(). Each draw still calls the real method. A real file or GPU resource also needs an explicit disposal owner.
Related reading
Each example is a separate .NET 10 console program.
Footnotes
- Gamma, Helm, Johnson, Vlissides. Design Patterns (1994). Publisher record. Structural patterns, Proxy. β©