songining

์ฝ”ํ‹€๋ฆฐ์€ ํ”„๋กœํผํ‹ฐ ์œ„์ž„ ์ด๋ผ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

ํ”„๋กœํผํ‹ฐ ์œ„์ž„์„ ์‚ฌ์šฉํ•˜๋ฉด ์ผ๋ฐ˜์ ์ธ ํ”„๋กœํผํ‹ฐ์˜ ํ–‰์œ„๋ฅผ ์ถ”์ถœํ•ด์„œ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

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)
}

'Language > Kotlin' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Kotlin] associateBy, groupBy  (0) 2023.02.21
Kotlin Coroutine Context, Coroutine Builder  (0) 2022.11.01
Kotlin inline fun, reified  (0) 2022.10.28