kotlinintermediate

Interface Delegation with 'by'

Delegate interface implementations with the 'by' keyword: composition over inheritance patterns.

kotlin
interface Logger {
    fun log(level: String, message: String)
    fun info(message: String) = log("INFO", message)
    fun error(message: String) = log("ERROR", message)
    fun debug(message: String) = log("DEBUG", message)
}

class ConsoleLogger : Logger {
    override fun log(level: String, message: String) {
        println("[$level] $message")
    }
}

class FileLogger(private val filename: String) : Logger {
    override fun log(level: String, message: String) {
        println("[FILE:$filename] [$level] $message")
    }
}

// Delegation — forward Logger calls to provided implementation
class UserService(logger: Logger) : Logger by logger {
    fun createUser(name: String) {
        info("Creating user: $name")
        // business logic
        info("User created: $name")
    }
}

// Delegation with override
class AuditedService(private val inner: Logger) : Logger by inner {
    private val auditLog = mutableListOf<String>()

    override fun log(level: String, message: String) {
        auditLog.add("$level: $message")
        inner.log(level, message) // forward to delegate
    }

    fun getAuditLog(): List<String> = auditLog.toList()
}

// Multiple delegations
interface Cache<K, V> {
    fun get(key: K): V?
    fun put(key: K, value: V)
    fun clear()
}

class InMemoryCache<K, V> : Cache<K, V> {
    private val store = mutableMapOf<K, V>()
    override fun get(key: K): V? = store[key]
    override fun put(key: K, value: V) { store[key] = value }
    override fun clear() { store.clear() }
}

interface Metrics {
    fun increment(name: String)
    fun getCount(name: String): Int
}

class SimpleMetrics : Metrics {
    private val counts = mutableMapOf<String, Int>()
    override fun increment(name: String) { counts[name] = (counts[name] ?: 0) + 1 }
    override fun getCount(name: String) = counts[name] ?: 0
}

// Compose multiple interfaces via delegation
class CachedApiService(
    cache: Cache<String, String>,
    metrics: Metrics,
    logger: Logger
) : Cache<String, String> by cache,
    Metrics by metrics,
    Logger by logger {

    fun fetchData(key: String): String {
        increment("fetch_total")
        val cached = get(key)
        if (cached != null) {
            increment("cache_hit")
            info("Cache hit: $key")
            return cached
        }
        increment("cache_miss")
        info("Cache miss: $key — fetching...")
        val data = "data-for-$key" // simulate fetch
        put(key, data)
        return data
    }

    fun stats() {
        info("Fetches: ${getCount("fetch_total")}")
        info("Hits: ${getCount("cache_hit")}")
        info("Misses: ${getCount("cache_miss")}")
    }
}

fun main() {
    // Simple delegation
    val service = UserService(ConsoleLogger())
    service.createUser("Alice")

    // Audited
    println("\n--- Audited ---")
    val audited = AuditedService(ConsoleLogger())
    audited.info("Starting")
    audited.error("Something failed")
    println("Audit log: ${audited.getAuditLog()}")

    // Composed service
    println("\n--- Composed ---")
    val api = CachedApiService(
        InMemoryCache(),
        SimpleMetrics(),
        ConsoleLogger()
    )
    api.fetchData("users")
    api.fetchData("users") // cache hit
    api.fetchData("posts")
    api.stats()
}

Use Cases

  • Composition over inheritance
  • Decorator/proxy pattern implementation
  • Cross-cutting concern composition

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.