Kotlin Design Patterns: Simplifying the Proxy Pattern
Tags: kotlin, java, backend, designpatterns
Published: 2024-02-20 15:45:49

The Proxy Pattern is a structural design pattern in object-oriented programming. It provides a surrogate or placeholder for another object to control access to it. This pattern creates a 'proxy' object that acts as an intermediary between the client and the real object.

This pattern is useful when direct access to an object is impractical or impossible. For example: when an object is remote, expensive to create, or needs additional security.

It's widely used in software development for several key purposes. The main use cases are:

1.  **Lazy Initialization (Virtual Proxy):** Delays the creation of expensive objects until necessary, saving resources.
    
2.  **Access Control (Protection Proxy):** Manages access to an object, allowing or denying operations based on permission checks.
    
3.  **Remote Object Access (Remote Proxy):** Represents an object in a different location, handling communication in networked applications.
    
4.  **Logging Requests (Logging Proxy):** Records operations on an object, useful for debugging and monitoring.
    
5.  **Caching:** Stores results of operations, returning cached data for repeated requests to enhance performance.

Let's dive into two of those use cases. In these examples, we will not consider coupling, orthogonality and best practices because we want to focus on the pattern itself.


### Lazy Initialization (Virtual Proxy) example

#### The base classes:

Let's consider a very simple and hypothetic `DatabaseQuery` interface and its implementation. 

The **Java** solution is usually like this:
```java
interface DatabaseQuery { 
    String execute(); 
}

class RealDatabaseQuery implements DatabaseQuery {
    public String execute() {
        return "Query Result";
    }
}
```
In **Kotlin**, we would have this equivalent code:
```kotlin
interface DatabaseQuery {
    fun execute(): String
}

class RealDatabaseQuery : DatabaseQuery {
    override fun execute() = "Query Result"
}
```

#### The proxy class:
The following **Java** class allows for a lazy `DatabaseQuery` that will instantiate the `RealDatabaseQuery` only when (and if) needed - that is: when `execute()` is called.
```java
class LazyDatabaseQuery implements DatabaseQuery {
    private RealDatabaseQuery realQuery = null;

    public String execute() {
        if (realQuery == null) {
            realQuery = new RealDatabaseQuery();
        }
        return realQuery.execute();
    }
}
```
In **Kotlin**, you can use lazy initialization feature to simplify the implementation of the Lazy Initialization Proxy:
```kotlin
class LazyDatabaseQuery : DatabaseQuery {
    private val realQuery by lazy { RealDatabaseQuery() }

    override fun execute() = realQuery.execute()
}
```
The `lazy` keyword is used for lazy initialization of `RealDatabaseQuery`. This ensures that `RealDatabaseQuery` is only created when it's first needed, adhering to the principles of the Lazy Initialization Proxy pattern.

### Logging Requests (Logging Proxy) example

This proxy logs each action performed on an object. It's useful for monitoring activities, debugging, or auditing system usage.

#### The base classes:

Let's define an interface `DataService` with a method `fetchData()`. Wel'll also define `RealDataService`, a class implementing this interface, where `fetchData()` would fetch and return data from a data source.

```java
public interface DataService {
    String fetchData();
    String fetchStatistics();
}

public class RealDataService implements DataService {
    public String fetchData() {
        return "Data from service";
    }
    public String fetchStatistics() {
        return "999";
    }
}
```
In **Kotlin**, we would have this equivalent code:
```kotlin
interface DataService {
    fun fetchData(): String
    fun fetchStatistics(): String
}

class RealDataService : DataService {
    override fun fetchData() = "Data from service"
    override fun fetchStatistics(): String = "999"
}
```

#### The proxy class:

The `LoggingDataServiceProxy` **Java** class in implements a proxy for `DataService`. It logs a message each time its `fetchData` method is called, then delegates the actual data fetching to the encapsulated `DataService` instance.
```java
public class LoggingDataServiceProxy implements DataService {
    private DataService service;

    public LoggingDataServiceProxy(DataService service) { this.service = service } 

    public String fetchData() {
        System.out.println("Fetching data...");
        return service.fetchData();
    }

	public String fetchStatistics() {
        return service.fetchData();
    }
}
```

In **Kotlin**, we can make use of class delegation to easily intercept and log only the desired methods:
```kotlin
class LoggingDataServiceProxy(private val realService: DataService) : DataService by realService {
    override fun fetchData(): String {
        println("LOG: Fetching data...")
        return realService.fetchData()
    }
}
```
Kotlin's class delegation (`by realService`) simplifies forwarding calls while allowing override for logging.

## Final Thoughts
In Kotlin, the Proxy Pattern is enhanced (or even made unnecessary) by language features like class delegation, delegated properties and lazy initialization. These features allow for more concise and expressive implementations.


--
This article was originally posted to my Lucas Fugisawa on Kotlin blog, at: https://fugisawa.com/kotlin-design-patterns-simplifying-the-proxy-pattern/

To explore more about design patterns and other Kotlin-related topics, subscribe to my newsletter on https://fugisawa.com/ and stay tuned for more insights and updates.