Don’t Reinvent the Wheel, Delegate It! - статья об использовании встроенных в язык Kotlin средств делегирования реализаций классов и свойств.
Один из ключевых постулатов современного ООП-программирования гласит: "Предпочитайте делегирование наследованию". Это значит, что вместо наследования от какого-либо класса лучше включить инстанс этого класса в другой класс и вызывать его методы при вызове одноименных методов этого класса:
class Class1() {
fun method1() { ... }
fun method1() { ... }
}
class Class2(firstClass: Class1) {
private val class1 = firstClass
fun method1() { firstClass.method1() }
fun method2() { firstClass.method2() }
}
Зачем это нужно? Для того, чтобы избежать проблем когда методы класса-родителя вызывают друг друга. Если method1() вызывает method2(), то переопределив второй метод мы сломаем работу первого. Делегирование решает эту проблему так как Class1 и Class2 остаются не связанными друг с другом.
С другой стороны делегирование усложняет код и поэтому в Kotlin есть специальное ключевое слово by, сильно упрощающее жизнь разработчику. Благодаря ему реализовать второй класс можно с помощью всего одной строки:
class Class2(firstClass: Class1) : Class1 by firstClass
Это действительно все. Компилятор Kotlin автоматически преобразует эту строку в аналог реализации Class2 из первого примера.
Kotlin также позволяет делегировать реализацию свойств. В следующей записи используется стандартный делегат lazy, инициализирующий переменную при первом обращении к ней:
val orm by lazy { KotlinORM("main.db") }
Еще более интересно работает делегат map, позволяющий магическим образом взять значения из хэшмепа.
class User(val map: Map) {
val name: String by map
val age: Int by map
}
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))
println(user.name) // "John Doe"
println(user.age) // 25
Кроме lazy и map стандартная библиотека Kotlin включает в себя еще три стандартных делегата:
* nutNull - аналог ключевого слова lateinit с более широкими возможностями;
* observable - позволяет выполнить код в момент чтения или записи переменной;
* vetoable - похож на observable, но срабатывает перед записью нового значение и может запретить изменение.
Ну и конечно же любой разработчик может создать собственный делегат. Это всего-лишь класс c реализацией операторов getValue() и setValue():
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}
Один из ключевых постулатов современного ООП-программирования гласит: "Предпочитайте делегирование наследованию". Это значит, что вместо наследования от какого-либо класса лучше включить инстанс этого класса в другой класс и вызывать его методы при вызове одноименных методов этого класса:
class Class1() {
fun method1() { ... }
fun method1() { ... }
}
class Class2(firstClass: Class1) {
private val class1 = firstClass
fun method1() { firstClass.method1() }
fun method2() { firstClass.method2() }
}
Зачем это нужно? Для того, чтобы избежать проблем когда методы класса-родителя вызывают друг друга. Если method1() вызывает method2(), то переопределив второй метод мы сломаем работу первого. Делегирование решает эту проблему так как Class1 и Class2 остаются не связанными друг с другом.
С другой стороны делегирование усложняет код и поэтому в Kotlin есть специальное ключевое слово by, сильно упрощающее жизнь разработчику. Благодаря ему реализовать второй класс можно с помощью всего одной строки:
class Class2(firstClass: Class1) : Class1 by firstClass
Это действительно все. Компилятор Kotlin автоматически преобразует эту строку в аналог реализации Class2 из первого примера.
Kotlin также позволяет делегировать реализацию свойств. В следующей записи используется стандартный делегат lazy, инициализирующий переменную при первом обращении к ней:
val orm by lazy { KotlinORM("main.db") }
Еще более интересно работает делегат map, позволяющий магическим образом взять значения из хэшмепа.
class User(val map: Map) {
val name: String by map
val age: Int by map
}
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))
println(user.name) // "John Doe"
println(user.age) // 25
Кроме lazy и map стандартная библиотека Kotlin включает в себя еще три стандартных делегата:
* nutNull - аналог ключевого слова lateinit с более широкими возможностями;
* observable - позволяет выполнить код в момент чтения или записи переменной;
* vetoable - похож на observable, но срабатывает перед записью нового значение и может запретить изменение.
Ну и конечно же любой разработчик может создать собственный делегат. Это всего-лишь класс c реализацией операторов getValue() и setValue():
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}