0Pricing
Kotlin Academy · Урок

Делегаты observable и vetoable

Реагируйте на изменения свойств с помощью observable и условно отклоняйте их с помощью vetoable.

«Делегаты observable и vetoable» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.

Повторение делегатов свойств

Делегаты свойств Kotlin позволяют отдельному объекту обрабатывать getValue и setValue. Delegates.observable и Delegates.vetoable — встроенные делегаты стандартной библиотеки.

import kotlin.properties.Delegates
var name: String by Delegates.observable("initial") { prop, old, new ->
    println("${prop.name}: $old -> $new")
}
fun main() { name = "Alice"; name = "Bob" }

Синтаксис observable

observable принимает начальное значение и функцию обратного вызова, которая вызывается после каждого изменения и получает свойство, старое значение и новое значение.

import kotlin.properties.Delegates
var score: Int by Delegates.observable(0) { _, old, new ->
    println("Score changed from $old to $new")
}
fun main() {
    score = 10
    score = 25
    score = 0
}

Запуск обновлений пользовательского интерфейса

Наблюдаемые делегаты отлично подходят для запуска обновления пользовательского интерфейса при изменении базового свойства без полной настройки LiveData.

import kotlin.properties.Delegates
class ViewModel {
    var title: String by Delegates.observable("Loading") { _, _, new ->
        println("Render: $new") // pretend this updates UI
    }
}
fun main() {
    val vm = ViewModel()
    vm.title = "Home"
    vm.title = "Settings"
}

Синтаксис vetoable

vetoable вызывает функцию обратного вызова до присваивания. Если функция обратного вызова возвращает false, новое значение отклоняется, а старое сохраняется.

import kotlin.properties.Delegates
var age: Int by Delegates.vetoable(0) { _, old, new ->
    new >= 0 // reject negative ages
}
fun main() {
    age = 25; println(age)  // 25
    age = -1; println(age)  // 25 (rejected)
    age = 30; println(age)  // 30
}

Объединение проверки с vetoable

Используйте vetoable, чтобы обеспечивать инварианты предметной области на уровне свойства без внешней логики проверки.

import kotlin.properties.Delegates
var email: String by Delegates.vetoable("") { _, _, new ->
    new.contains("@")
}
fun main() {
    email = "user@example.com"; println(email) // set
    email = "invalid";          println(email) // still previous
}

Журналирование изменений с помощью observable

Используйте observable для ведения журнала аудита: автоматически записывайте каждый переход состояния, не затрагивая бизнес-логику.

import kotlin.properties.Delegates
val log = mutableListOf<String>()
var status: String by Delegates.observable("IDLE") { _, old, new ->
    log.add("$old -> $new")
}
fun main() {
    status = "RUNNING"
    status = "DONE"
    println(log)
}

observable в классе

Оба делегата работают внутри тел классов так же, как и любое другое свойство.

import kotlin.properties.Delegates
class Counter {
    var count: Int by Delegates.observable(0) { _, old, new ->
        if (new > old) println("Incremented to $new")
        else println("Decremented to $new")
    }
}
fun main() {
    val c = Counter()
    c.count = 5
    c.count = 3
}

vetoable для ограничения диапазона

Ограничивайте числовое свойство допустимым диапазоном с помощью vetoable.

import kotlin.properties.Delegates
var volume: Int by Delegates.vetoable(50) { _, _, new ->
    new in 0..100
}
fun main() {
    volume = 80;  println(volume) // 80
    volume = 150; println(volume) // 80 (rejected)
    volume = 0;   println(volume) // 0
}

Цепочки делегатов

Логику observable можно объединять вручную: напишите собственный делегат, который вызывает observable внутри себя и добавляет дополнительную логику на каждом шаге.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class LoggedDelegate<T>(initial: T) : ReadWriteProperty<Any?, T> {
    private var value: T = initial
    override fun getValue(t: Any?, p: KProperty<*>) = value
    override fun setValue(t: Any?, p: KProperty<*>, v: T) {
        println("Setting ${p.name} = $v"); value = v
    }
}
var x: Int by LoggedDelegate(0)
fun main() { x = 10; x = 20 }

Примечание о безопасности потоков

Ни observable, ни vetoable по умолчанию не являются потокобезопасными. При параллельном доступе защищайте изменения с помощью мьютекса или выполняйте внешнюю синхронизацию.

import kotlin.properties.Delegates
// Not thread-safe as-is:
var sharedState: String by Delegates.observable("start") { _, old, new ->
    println("$old -> $new")
}
// In coroutines, wrap access with Mutex if needed

Практический шаблон: проверка формы

Используйте vetoable для каждого поля, чтобы декларативно проверять форму: каждое поле самостоятельно проверяет себя при присваивании.

import kotlin.properties.Delegates
data class Form(
    var username: String = "",
    var password: String = ""
) {
    var validUsername: String by Delegates.vetoable("") { _,_,v -> v.length >= 3 }
    var validPassword: String by Delegates.vetoable("") { _,_,v -> v.length >= 8 }
}
fun main() {
    val f = Form()
    f.validUsername = "ab";       println(f.validUsername) // ""
    f.validUsername = "alice";    println(f.validUsername) // "alice"
    f.validPassword = "short";    println(f.validPassword) // ""
    f.validPassword = "secure123";println(f.validPassword) // "secure123"
}

Быстрая проверка

Когда vetoable вызывает свою функцию обратного вызова?

Итоги

observable уведомляет после каждого изменения; vetoable выполняется до присваивания и может отклонить новое значение. Оба делегата отлично подходят для ведения журналов, проверки и реактивных схем работы со свойствами без внешних библиотек.

Часто задаваемые вопросы

Урок «Делегаты observable и vetoable» бесплатный?

Да — полный текст урока «Делегаты observable и vetoable» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.

Чему я научусь в уроке «Делегаты observable и vetoable»?

Реагируйте на изменения свойств с помощью observable и условно отклоняйте их с помощью vetoable. Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Kotlin Academy?

Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Делегаты observable и vetoable»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Kotlin Academy?

Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. by lazy: подробный разбор отложенной инициализации
  2. Делегаты observable и vetoable
  3. Написание пользовательского делегата ReadWriteProperty
  4. Делегаты для настроек, свойств на основе карт и ведения журнала
← Назад к Kotlin Academy