[Effective Kotlin] 아이템 21. 일반적인 프로퍼티 패턴은 프로퍼티 위임으로 만들어라

Jimin Lim·2023년 8월 3일
0

Effective Kotlin

목록 보기
21/39
post-thumbnail

아이템 21

일반적인 프로퍼티 패턴은 프로퍼티 위임(Delegated Properties)으로 만들어라

프로퍼티 위임

  • 일반적인 프로퍼티 행위를 추출해서 재사용할 수 있다.
  • set, get의 호출을 위임할 수 있다.

lazy property

지연 프로퍼티: 이후에 처음 사용하는 요청이 들어올 때 초기화되는 프로퍼티

val value by lazy { createValue() }

observable

import android.util.Log
import kotlin.properties.Delegates

val items: List<Item> by Delegates.observable(listOf()) { _, _, _ ->
    notifyDataSetChanged()
}

val key: String? by Delegates.observable(null) { _, old, new ->
    Log.e("TAG", "key changed from $old to $new")
}

위와 같이 observable을 사용해 작성한다면 내부 데이터가 변경될 때마다 변경된 내용을 출력하고, 변경 사항을 로그로 작성할 수 있다.

그 외 다양한 패턴

  • bindView: 안드로이드에서 뷰와 리소스 바인딩
  • inject: Koin에서 종속성 주입
  • bindConfiguration: 데이터 바인딩

프로퍼티 위임을 사용해 간단하고 type-safe하게 구현할 수 있다.

구현 예시

var token: String? = null
    get() {
        print("token returned value $field")
        return field
    }
    set(value) {
        print("token changed from $field to $value")
        field = value
    }

var attempts: Int = 0
    get() {
        print("attempts returned value $field")
        return field
    }
    set(value) {
        print("attempts changed from $field to $value")
        field = value
    }

위의 코드는 get이나 set할때 print() 문을 남긴다는 공통점이 있다. 공통적으로 자주 반복되는 패턴은 프로퍼티 위임으로 추출하기 좋다.

getValue(), setValue()를 정의해 프로퍼티 위임을 구현할 수 있다.

var token: String? by LoggingProperty(null)
var attempts: Int by LoggingProperty(0)

private class LoggingProperty<T>(var value: T) {
    operator fun getValue(
        thisRef: Any?,
        prop: KProperty<*>
    ): T {
        print("${prop.name} returned value $value")
        return value
    }

    operator fun setValue(
        thisRef: Any?,
        prop: KProperty<*>,
        newValue: T
    ) {
        val name = prop.name
        print("$name changed from $value to $newValue")
        value = newValue
    }
}

map에 활용

val map: Map<String, Any> = mapOf(
    "name" to "Marcin",
    "kotlinProgrammer" to true
)

fun main() {
    val name by map
    print(name) // Marcin
}
inline operator fun <V, V1: V> Map<in String, V>
        .getValue(thisRef: Any?, property: KProperty<*>): V1 =
    getOrImplicitDefault(property.name) as V1

프로퍼티를 map에 위임하면 getValue에서 맵에 프로퍼티를 값을 저장할 때는 자동으로 프로퍼티 이름을 키로 활용한다.

profile
💻 ☕️ 🏝 🍑 🍹 🏊‍♀️

0개의 댓글