Motivation: We need to control access to an object. For example, to defer its creation until it’s actually needed (lazy initialization), to check permissions, or to cache results.

Intent: Provide a surrogate or placeholder for another object to control access to it.

The proxy should implement the same interface as the read object so interacting with the proxy is the same as interacting with the real thing.

interface Image {
    fun display()
}
 
class RealImage(private val filename: String) : Image {
    init { loadFromDisk() }
    private fun loadFromDisk() { /* expensive loading... */ }
    override fun display() { /* render image */ }
}
 
class LazyImageProxy(private val filename: String) : Image {
    private var realImage: RealImage? = null
    override fun display() {
        if (realImage == null) realImage = RealImage(filename)
        realImage!!.display()
    }
}