Proxy Design Pattern in Kotlin

1. Definition

The Proxy Design Pattern provides a surrogate or placeholder for another object to control access to it. It can add a layer of protection to the real object from external operations.

2. Problem Statement

Suppose you have an application where you need to control access to some resources, perhaps because they are expensive to create, or you want to add some additional operations before or after the main operation.

3. Solution

The Proxy pattern suggests creating a new proxy class with the same interface as the original service object. This new proxy class can then be used to wrap the original object and add additional behaviors to it.

4. Real-World Use Cases

1. Lazy loading of large-size images in a graphics editor.

2. Access control when managing user permissions.

3. Monitoring access to an object and logging operations.

5. Implementation Steps

1. Create an interface that both the real object and the proxy will implement.

2. Implement the real object that will perform the main operations.

3. Implement the proxy object that will add additional behaviors and control access to the real object.

6. Implementation in Kotlin Programming

// Step 1: Interface
interface Database {
    fun query(dbQuery: String): String
}
// Step 2: Real Object
class RealDatabase : Database {
    override fun query(dbQuery: String): String {
        // Simulating a database operation
        return "Executing query: $dbQuery"
    }
}
// Step 3: Proxy Object
class ProxyDatabase : Database {
    private val realDatabase = RealDatabase()
    private val restrictedQueries = listOf("DROP", "DELETE")
    override fun query(dbQuery: String): String {
        if (restrictedQueries.any { dbQuery.contains(it, ignoreCase = true) }) {
            return "Query not allowed!"
        }
        // Logging operation
        println("Logging: $dbQuery")
        return realDatabase.query(dbQuery)
    }
}
fun main() {
    val database = ProxyDatabase()
    println(database.query("SELECT * FROM users"))
    println(database.query("DROP TABLE users"))
}

Output:

Logging: SELECT * FROM users
Executing query: SELECT * FROM users
Query not allowed!

Explanation:

1. Database is the common interface for RealDatabase and ProxyDatabase.

2. RealDatabase performs the real operations.

3. ProxyDatabase controls access to RealDatabase. It adds a layer of logging and restricts certain queries.

4. In the main function, when using ProxyDatabase, the SELECT query is logged and executed, but the DROP query is restricted and not executed.

7. When to use?

The Proxy pattern is useful when:

1. You want to add additional behaviors (like logging or caching) to individual operations in an object without changing its code.

2. You want to delay the initialization of an expensive object and load it on demand (lazy loading).

3. You want to control access to the object based on certain conditions.

Comments