Proxy

프록시 패턴은 특정 객체에 대한 접근을 제어하거나 기능을 추가 할 수 있는 패턴을 의미한다.

부가 기능 추가가 목적인 Proxy 패턴을 Decorator 패턴이라고 한다.

  1. Subject: This is the interface or abstract class that defines the operations that the real object and the proxy must implement.
  2. RealSubject: This is the real object that the proxy represents. It implements the operations defined in the subject interface.
  3. Proxy: This is the object that acts as a surrogate or placeholder for the real object. It implements the subject interface and delegates the operations to the real object.

코드로 보면 다음과 같다.

Subject:

interface Subject {
    fun doSomething(): Void
}

Real Subject:

class RealSubject: Subject {
    override fun doSomething() {
        // doSomething
    }
}

Proxy class:

class Proxy: Subject {
    private val realSubject = RealSubject()

    override fun doSomething(): Void {
        // doSomething (caching, logging ... 
        return realSubject.doSomething() // delegation
    }
}

이러한 방식으로 구현해서 사용하면 된다.

Remote Proxy

원격 프록시(Remote Proxy)는 프록시는 로컬에 있고, RealSubject 가 원격 서버(다른 서버)에 존재하는 경우 사용할 수 있다. 프록시는 네트워크 통신을 통해 클라이언트 요청을 RealSubject 로 전달한다.

Subjects:

interface ProductService {
    fun getProducts(): List<Product>
}

Next, we define the real subject class that implements the ProductService interface and fetches the list of products over the network:

class RemoteProductService : ProductService {
    override fun getProducts(): List<Product> {
        // Fetch products over the network
        Thread.sleep(5000) // Simulate network delay
        return listOf(Product("Product 1"), Product("Product 2"), Product("Product 3"))
    }
}

data class Product(val name: String)

Now, we define the remote proxy class that implements the ProductService interface and delegates the operations to the real subject class:

class RemoteProductServiceProxy : ProductService {
    private val realService = RemoteProductService()

    override fun getProducts(): List<Product> {
        // Check if the list of products is already cached
        // If yes, return the cached list
        // Otherwise, fetch the list from the remote service
        return realService.getProducts()
    }
}

Finally, we can use the remote proxy to fetch the list of products without incurring the overhead of network communication:

fun main() {
    val productService: ProductService = RemoteProductServiceProxy()

    // Fetch the list of products
    val products = productService.getProducts()

    // Print the list of products
    println("Products:")
    products.forEach { println(it.name) }
}

Virtual Proxy

객체 생성 작업이 무거울때 사용하는 Lazy Initialization 방식

First, we define the subject interface that defines the operations for loading and displaying the image:

interface Image {
    fun display()
}

Next, we define the real subject class that implements the Image interface and loads the actual image:

class RealImage(private val filename: String) : Image {
    init {
        loadFromDisk()
    }

    private fun loadFromDisk() {
        // Load the image from the disk
        println("Loading image from disk: $filename")
    }

    override fun display() {
        // Display the image
        println("Displaying image: $filename")
    }
}

Now, we define the virtual proxy class that implements the Image interface and creates the real subject object only when it is needed:

class VirtualImage(private val filename: String) : Image {
    private var realImage: RealImage? = null

    override fun display() {
        // Create the real image object only when it is needed
        if (realImage == null) {
            realImage = RealImage(filename)
        }

        // Display the image
        realImage?.display()
    }
}

Protection Proxy

보호 프록시의 목적은 접근 권한을 제어하는 것이다.

First, we define the subject interface that defines the operations for accessing the information:

interface InformationService {
    fun getInformation(): String
}

Next, we define the real subject class that implements the InformationService interface and provides access to the sensitive information:

class RealInformationService : InformationService {
    override fun getInformation(): String {
        return "Sensitive information"
    }
}

Now, we define the protection proxy class that implements the InformationService interface and checks the permissions before granting access to the sensitive information:

class ProtectionInformationService(private val informationService: InformationService, private val user: String) : InformationService {
    override fun getInformation(): String {
        // Check if the user has the proper permissions
        if (user == "admin") {
            // Grant access to the sensitive information
            return informationService.getInformation()
        } else {
            // Deny access to the sensitive information
            throw SecurityException("Access denied")
        }
    }
}

Finally, we can use the protection proxy to restrict access to the sensitive information:

fun main() {
    // Create the real subject object
    val realService: InformationService = RealInformationService()

    // Create a protection proxy for the information service
    val informationService: InformationService = ProtectionInformationService(realService, "admin")

    // Get the sensitive information
    val information = informationService.getInformation()

    // Print the sensitive information
    println("Information: $information")
}