Proxy Design Pattern in Scala

1. Definition

The Proxy Design Pattern provides a surrogate or placeholder for another object to control access to it. It can be thought of as an object functioning as an interface to something else.

2. Problem Statement

Suppose you have an object that is expensive to create, or it needs to be controlled when accessed, or you need to add functionalities like lazy initialization, logging, access control, etc., when accessing the actual object.

3. Solution

The solution is to have a separate proxy object that stands as an intermediary between the client and the actual object. This proxy will control the access to the original object, manage its lifecycle, and may even decide to create it only when it's actually required.

4. Real-World Use Cases

1. Image loading in a graphic editor: the proxy can display a placeholder until the real image is loaded.

2. Access control for a remote object, adding a layer of security.

5. Implementation Steps

1. Define an interface that both the RealObject and Proxy will implement.

2. Create the RealObject which contains the actual business logic.

3. Create the Proxy class which holds a reference to the RealObject and controls its access and lifecycle.

6. Implementation in Scala Programming

// Interface both RealObject and Proxy implement
trait Image {
  def display(): Unit
}
// RealObject with actual business logic
class RealImage(val filename: String) extends Image {
  private def loadFromDisk(): Unit = println(s"Loading $filename")
  loadFromDisk()
  override def display(): Unit = println(s"Displaying $filename")
}
// Proxy Object
class ProxyImage(val filename: String) extends Image {
  private var realImage: RealImage = _
  override def display(): Unit = {
    if(realImage == null) {
      realImage = new RealImage(filename)
    }
    realImage.display()
  }
}
// Client
object ImageClient extends App {
  val image: Image = new ProxyImage("test.jpg")
  image.display()  // Image will be loaded and displayed
  image.display()  // Image won't be loaded again, just displayed
}

Output:

Loading test.jpg
Displaying test.jpg
Displaying test.jpg

Explanation:

1. Image is the interface both the RealObject (RealImage) and the Proxy (ProxyImage) implement.

2. The RealImage loads the image right when it's instantiated and can display it.

3. The ProxyImage only loads the image when it's actually required (i.e., when the display method is called). On subsequent calls, it reuses the loaded image.

4. The client uses the proxy, so the real image is lazily loaded on the first display call.

7. When to use?

Use the Proxy pattern when:

1. You need a more versatile or sophisticated reference to an object than a simple pointer.

2. You want to postpone the creation and initialization of an expensive object until it's actually needed.

3. You want to provide a level of access control to the actual object.


Comments