Delegation Pattern in Kotlin
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 by
keyword.
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 by
keyword. 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
- Delegation | Kotlin Documentation
https://kotlinlang.org/docs/delegation.html - Delegated properties | Kotlin Documentation
https://kotlinlang.org/docs/delegated-properties.html