Kotlin ์์ ํ๋กํผํฐ Observable
์ฝํ๋ฆฐ์ ํ๋กํผํฐ ์์ ์ด๋ผ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
ํ๋กํผํฐ ์์์ ์ฌ์ฉํ๋ฉด ์ผ๋ฐ์ ์ธ ํ๋กํผํฐ์ ํ์๋ฅผ ์ถ์ถํด์ ์ฌ์ฌ์ฉํ ์ ์๋ค.
Observable
๋ฐ์ดํฐ์ ๋ณ๊ฒฝ์ด ์์ ๋ ๋ณ๊ฒฝ์ ๊ฐ์งํ์ฌ callback์ผ๋ก ์ํ๋ ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๋ค.
Delegates.observable ํจ์๋ฅผ ๋ณด๋ฉด 2๊ฐ์ ์ธ์๋ฅผ ๋ฐ์ผ๋ฉฐ ์ด๊ธฐ๊ฐ๊ณผ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ด ์์ ๋ ํธ์ถ๋๋ ์ฝ๋ฐฑ ํจ์ (onChange) ๋ก ์ด์ฐ๋ฌ์ ธ์๋ค.
public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):
ReadWriteProperty<Any?, T> =
object : ObservableProperty<T>(initialValue) {
override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
}
ํ๋กํผํฐ์ ๋ณ๊ฒฝ์ ๊ฐ์งํ๊ณ ์ถ๋ค๋ฉด Delegates.ovservable์ ์์ํด ์ํ๋ ํ๋กํผํฐ์ ๋ณ๊ฒฝ์ ์ฝ๊ฒ ๊ฐ์งํ ์ ์๋ค.
์ฝ๋ฐฑ ํจ์๋ ์ด 3๊ฐ์ ์ธ์๋ฅผ ๋ฐ๋๋ค.
prop : ํ๋กํผํฐ ์ ๋ณด (KProperty)
old : ๋ณ๊ฒฝ ์ ๊ฐ
new : ์๋ก ์ค์ ๋ ๊ฐ
// ์ฌ์ฉ ์์
var items: Item by Delegates.observable(Item()) {
prop, old, new ->
println("$old -> $new")
}
ObservableProperty
Delegates.observable์ ObservableProperty ์ ๊ตฌํ์ฒด์ด๋ค.
public abstract class ObservableProperty<V>(initialValue: V) : ReadWriteProperty<Any?, V> {
private var value = initialValue
/**
* The callback which is called before a change to the property value is attempted.
* The value of the property hasn't been changed yet, when this callback is invoked.
* If the callback returns `true` the value of the property is being set to the new value,
* and if the callback returns `false` the new value is discarded and the property remains its old value.
*/
protected open fun beforeChange(property: KProperty<*>, oldValue: V, newValue: V): Boolean = true
/**
* The callback which is called after the change of the property is made. The value of the property
* has already been changed when this callback is invoked.
*/
protected open fun afterChange(property: KProperty<*>, oldValue: V, newValue: V): Unit {}
public override fun getValue(thisRef: Any?, property: KProperty<*>): V {
return value
}
public override fun setValue(thisRef: Any?, property: KProperty<*>, value: V) {
val oldValue = this.value
if (!beforeChange(property, oldValue, value)) {
return
}
this.value = value
afterChange(property, oldValue, value)
}
}
๋ง์ฝ ๋ ๋ณต์กํ ๋ณ๊ฒฝ ๋ก์ง์ ์ฒ๋ฆฌ๊ฐ ํ์ํ๋ค๋ฉด ํด๋น ์ถ์ ํด๋์ค๋ฅผ ์ง์ ์์๋ฐ์ ๊ตฌํํ ์ ์๋ค.
์๋ฅผ ๋ค๋ฉด, beforeChange ๋ถ๋ถ์์ ๋ณ๊ฒฝ์ ํ์ฉํ๊ฑฐ๋ ๊ฑฐ๋ถํ๋ ์กฐ๊ฑด์ ์ถ๊ฐํด์ค ์ ์๋ค.
(beforeChange ๊ฐ false์ด๋ฉด ํ๋กํผํฐ ๊ฐ ๋ณ๊ฒฝ์ด ๋ถ๊ฐํ๋ค.)
ObservableProperty ๋ ์ถ์ ํด๋์ค์ด๊ธฐ ๋๋ฌธ์ ํจ์ override๊ฐ ํ์ํ๋ค.
์๋ ์ฝ๋์ ๊ฐ์ด ์ฝํ๋ฆฐ์ ์ต๋ช ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํด ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ณ ์์ํด์ฃผ๋ฉด ๋ณ๋์ ํด๋์ค๋ฅผ ์์ฑํด์ฃผ์ง ์๊ณ ๋ ๋ฐ๋ก ๊ฐ์ฒด๋ฅผ ์ ์ํ๊ณ ์ฌ์ฉํ ์ ์๋ค.
// ์ฌ์ฉ ์์
override var age: Int by object : ObservableProperty<Int>(0) {
override fun beforeChange(property: KProperty<*>, oldValue: Int, newValue: Int): Boolean = newValue >= 0
override fun afterChange(property: KProperty<*>, oldValue: Int, newValue: Int) {
logger.info { "age changed to $newValue from $oldValue" }
}
}
๋ ๋ณต์กํ ์ฒ๋ฆฌ๊ฐ ํ์ํ๋ค๋ฉด ์ต๋ช ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ๊ฐ์ฒด๋ฅผ ์์ฑํด์ค ํ ์์ํด์ฃผ๋ฉด ๋๋ค.
class AgeProperty(var propValue: Int) : ObservableProperty<Int>(propValue) {
override fun beforeChange(property: KProperty<*>, oldValue: Int, newValue: Int): Boolean {
return newValue >= 0
}
override fun afterChange(property: KProperty<*>, oldValue: Int, newValue: Int) {
println("Age changed from $oldValue to $newValue")
}
}
class User {
var age: Int by AgeProperty(0)
}