โ† Back to Design Patterns
๐Ÿชž
Structural

Proxy

Provides a placeholder that controls access to another object โ€” adding lazy loading, caching, or access checks.

The Problem

A heavyweight object (network call, database, large file) should not be created or accessed until absolutely needed. You want to add access control or caching without touching the original class.

How It Works

  • โ€บClient talks to the proxy through the same interface as the real object
  • โ€บVirtual proxy: defers expensive creation until first use (lazy initialization)
  • โ€บCaching proxy: remembers results and returns cached copies
  • โ€บProtection proxy: checks permissions before delegating
  • โ€บRemote proxy: represents an object in a different address space

When to Use

โœ“Lazy initialization of a resource-heavy object
โœ“Caching to avoid repeated expensive operations
โœ“Access control โ€” verify permissions before allowing an operation
โœ“Logging or monitoring access to a sensitive object

Real-World Examples

๐ŸŽ
iOS

lazy var properties, SDWebImage lazy image loading, CoreData faulting

๐Ÿค–
Android

by lazy delegate, Glide placeholder images, ViewModel lazy init

๐ŸŒ
Web

ES6 Proxy, service workers as network proxies, memoization helpers

Code Examples

Swift
protocol ImageLoader {
    func load(url: String) -> String
}

class RealImageLoader: ImageLoader {
    func load(url: String) -> String {
        print("Network: loading \(url)")
        return "ImageData(\(url))"
    }
}

// Caching proxy
class ImageLoaderProxy: ImageLoader {
    private let real  = RealImageLoader()
    private var cache = [String: String]()

    func load(url: String) -> String {
        if let cached = cache[url] { print("Cache hit"); return cached }
        let data = real.load(url: url)
        cache[url] = data
        return data
    }
}

let loader: ImageLoader = ImageLoaderProxy()
loader.load(url: "https://example.com/photo.jpg")  // Network load
loader.load(url: "https://example.com/photo.jpg")  // Cache hit