← All patterns
Structural

Proxy

Stand in for an expensive or sensitive object and control when, whether and how it is actually used.

What Is It?

A Proxy implements the same interface as a real object and sits in front of it. Clients think they are talking to the real thing, but the proxy decides what happens: it might delay creating the object, cache its results, check permissions, or forward the call across a network.

A virtual proxy is the most common flavour in games. A TextureProxy hands out a low-res placeholder immediately, kicks off the real load (from Addressables, disk or a CDN) only the first time someone asks, and then caches the result so every later request is instant.

A protection proxy uses the same shape to guard access. The proxy checks whether the caller is allowed, say whether a DLC skin is owned, before letting the request reach the real asset. The client code is identical in both cases; only the proxy’s rules differ.

When Is It Used?

Use a virtual proxy for anything heavy that may never be needed: high-res textures in an inventory grid, audio banks for areas the player has not reached, or remote profile pictures.

Use a protection proxy when the same API should behave differently depending on entitlement, platform, or debug state, without scattering checks through every caller.

Avoid it when the object is cheap or always needed right away. Lazy loading in that case just moves the hitch to a worse moment, in the middle of gameplay instead of a loading screen.

Interactive Demo

Click the controls and watch the objects collaborate. The console mirrors what the C# code below would log.

Code

Assets / Scripts / Structural/ Proxy ›InventorySlot.cs
using UnityEngine;
using UnityEngine.UI;

namespace Patterns.Structural.Proxy
{
    /// <summary>
    /// Client. Asks its provider for a texture every time it is shown.
    /// It cannot tell whether it holds a proxy or the real thing.
    /// </summary>
    public class InventorySlot : MonoBehaviour
    {
        [SerializeField] private RawImage icon;
        [SerializeField] private string assetKey = "Icons/DragonSword_4K";
        [SerializeField] private Texture2D placeholder;

        private ITextureProvider provider;

        private void Awake()
        {
            provider = new TextureProxy(assetKey, placeholder);
        }

        private void OnEnable()
        {
            icon.texture = provider.GetTexture();
            provider.Loaded += OnLoaded;
        }

        private void OnDisable()
        {
            provider.Loaded -= OnLoaded;
        }

        private void OnLoaded(Texture2D texture) => icon.texture = texture;
    }
}
4 files · namespace Patterns.Structural.ProxyC# · UTF-8 · LF

Advantages & Disadvantages

+ Advantages

  • Defers expensive work until it is actually needed, and caches it afterwards.
  • Access rules, logging or metrics live in one place instead of in every client.
  • Clients stay unchanged because the proxy shares the real object’s interface.

− Disadvantages

  • The first real access can cause a visible hitch unless loading is asynchronous.
  • Adds a layer of indirection that hides when work really happens, which can confuse profiling.
  • Cached objects need an explicit release strategy or memory quietly grows.

Tips

  1. 01Load asynchronously (Addressables.LoadAssetAsync, Resources.LoadAsync) and serve a placeholder while the task runs.
  2. 02Track an IsLoaded flag and a pending task so simultaneous requests share one load instead of starting several.
  3. 03Give the proxy a Release() method that calls Addressables.Release so memory can be reclaimed.
  4. 04Keep protection checks in a separate proxy from caching; stacking two proxies is cleaner than one that does both.
  5. 05Unity’s own AssetReference is effectively a proxy: a lightweight handle that loads the real asset on demand.