Delegation Pattern in Kotlin

Armando Picón
4 min readJan 27, 2025

--

Class delegation, property delegation, and more

Some days ago, I needed to break the implementation of a complex ViewModel. Looking for some ideas I’ve found the Delegation Pattern.

What is the Delegation pattern?

A design pattern where one object relies on another to perform specific tasks instead of doing it itself.

Why use it?

  • Promotes composition over inheritance
  • Allows better reuse of functionality
  • Simplifies code by avoiding duplication

Kotlin’s support for Delegation

Kotlin makes the Delegation Pattern easy by providing:

  • Class delegation
  • Property delegation

Class Delegation

In Kotlin, class delegation allows you to delegate the implementation of an interface to another object using the bykeyword.

interface Logger {
fun log(message: String)
}

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

class FileLogger : Logger {
override fun log(message: String) {
println("FileLogger: $message")
}
}

class Application(logger: Logger) : Logger by logger

fun main() {
val consoleLogger = ConsoleLogger()
val fileLogger = FileLogger()

// Pass the consoleLogger instance to Application
val app1 = Application(consoleLogger)
app1.log("Using console logger.") // ConsoleLogger: Using console logger.

// Pass the fileLogger instance to Application
val app2 = Application(fileLogger)
app2.log("Using file logger.") // FileLogger: Using file logger.
}

It is even possible to apply multiple implementations with the only restriction being that you have to decide which one to use during the object construction.

interface Engine {
fun start()
fun stop()
fun getStatus(): String
}

class ElectricEngine : Engine {
override fun start() = println("⚡ Electric motor starting")
override fun stop() = println("⚡ Electric motor stopping")
override fun getStatus() = "Electric Motor OK"
}

class BasicEngine : Engine {
override fun start() = println("🔥 Gas engine starting")
override fun stop() = println("🔥 Gas engine stopping")
override fun getStatus() = "Gas Engine OK"
}

class HybridCar(
electricEngine: Engine,
basicEngine: Engine,
useElectric: Boolean
) : Engine by when {
useElectric -> electricEngine
else -> basicEngine
}

Of course, you can apply multiple class delegations to a single class

interface ElectricCar {
fun chargeBattery()
fun driveElectric()
}

interface GasolineCar {
fun refuelTank()
fun driveGasoline()
}

class GenericElectricCar : ElectricCar {
override fun chargeBattery() {
println("ElectricCar: Charging the battery.")
}

override fun driveElectric() {
println("ElectricCar: Driving on electric power.")
}
}

class GenericGasolineCar : GasolineCar {
override fun refuelTank() {
println("GasolineCar: Refueling the gas tank.")
}

override fun driveGasoline() {
println("GasolineCar: Driving on gasoline.")
}
}

class HybridCar(
electricCar: ElectricCar,
gasolineCar: GasolineCar
) : ElectricCar by electricCar, GasolineCar by gasolineCar

Property delegation

In Kotlin, property delegation allows you to delegate the getter and setter logic of a property to another object. This is done using the bykeyword. Instead of directly managing the property, the responsibility is handed off to a “delegate” object that provides custom behavior for reading and writing the property.

Built-in property delegates

Kotlin provides some useful built-in property delegates:

  • Lazy initialization: lazy is a standard delegate for initializing a property lazily.
val myLazyValue: String by lazy {
println("Initializing...")
"Hello, Kotlin!"
}

fun main() {
println(myLazyValue) // Prints: Initializing... Hello, Kotlin!
println(myLazyValue) // Prints: Hello, Kotlin! (No reinitialization)
}
  • Observable properties: Delegates.observable allows you to react to property changes.
import kotlin.properties.Delegates

var observedProperty: String by Delegates.observable("Initial Value") { _, old, new ->
println("Changed from $old to $new")
}

fun main() {
observedProperty = "New Value" // Prints: Changed from Initial Value to New Value
}

Custom property delegates

You can create your own property delegates by implementing the getValue and setValue operators

import kotlin.reflect.KProperty

class CustomDelegate {
private var value: String = "Default"

operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return value
}

operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: String) {
value = newValue.uppercase()
}
}


var myProperty: String by CustomDelegate()

fun main() {
println(myProperty) // Prints: Default
myProperty = "hello"
println(myProperty) // Prints: HELLO
}

You can find Property Delegation usage in Compose

The function remember{} in Compose has been designed using the concept of delegation in mind. Here I show you a very simplified example of it.

import kotlin.reflect.KProperty

class RememberDelegate<T>(private val initializer: () -> T) {
private var value: T? = null

operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
if (value == null) {
value = initializer()
}
return value!!
}

operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) {
value = newValue
}
}


fun <T> customRemember(initializer: () -> T): RememberDelegate<T> {
return RememberDelegate(initializer)
}

fun main() {
var rememberedValue by customRemember { "Hello, World!" }
println(rememberedValue) // Prints: Hello, World!

rememberedValue = "New Value"
println(rememberedValue) // Prints: New Value
}

Conclusion

The Delegation Pattern in Kotlin is a powerful way to simplify code and promote composition over inheritance. Kotlin’s built-in delegation support makes implementing this pattern easy and elegant.

  • Promotes composition over inheritance
  • Allows better reuse of functionality
  • Simplifies code by avoiding duplication

NOTE: I’ve created a playlist for 3 videos I recorded showing all these examples work (those videos are in Spanish)

References

--

--

Armando Picón
Armando Picón

Written by Armando Picón

lifelong learner • 👨🏽‍💻 Android eng (ex: @uber ) • 🇵🇪 @ 🇨🇱 • @gdgopen • content creator @devpicon | @primosauda

No responses yet