[이펙티브 코틀린] Item16. 프로퍼티는 동작이 아니라 상태를 나타내야 한다.

2023. 10. 2. 18:57·Kotlin/이펙티브 코틀린
반응형

기본적으로 코틀린의 프로퍼티는 사용자 정의 게터와 세터를 가질 수 있습니다.

var name: String? = null
    get() = field?.toUpperCase()
    set(value) {
        if (!value.isNullOrBlank()) {
            field = value
        }
    }

 

이 코드에서 field라는 식별자를 확인할 수 있습니다.

이는 프로퍼티의 데이터를 저장해두는 백킹 필드(backing field)에 대한 레퍼런스입니다.

val을 사용해서 읽기 전용 프로퍼티를 만들 때는 field가 만들어지지 않습니다.

val fullName: String
    get() = "$name $surname"

 

var를 이용한 프로퍼티는 게터와 세터를 정의할 수 있으며, 이를 파생 프로퍼티(derived property)라고 부릅니다.

이처럼 코틀린의 모든 프로퍼티는 디폴트로 캡슐화되어 있습니다.

 

프로퍼티를 통한 getter, setter접근은 필드와 다를 수 있습니다.

예를 들어 아래처럼 코드를 작성할 수도 있습니다.

var date: Date
    get() = Date(millis) // 매번 Date 객체를 생성합니다.
    set(value){
        // 객체는 millis 필드만 가집니다.
        millis = value.time
    }

 

프로퍼티는 필드가 필요없으며, 오히려 프로퍼티는 개념적으로 접근자(getter/setter)를 나타냅니다.

이러한 원리로 코틀린은 인터페이스에도 프로퍼티를 정의할 수 있습니다.

interface Person {
    val name: String
}

 

이는 게터를 가질 거라는 것을 나타내며, 다음과 같이 오버라이드할 수 있습니다.

open class SuperComputer {
    open val theAnswer: Long = 42
}
class AppleComputer : SuperComputer() {
    override val theAnswer: Long = 1_800_275_2273
}

 

마찬가지의 이유로 프로퍼티를 위임할 수도 있습니다.

val db: Database by lazy { connectDb() }

 

프로퍼티는 본질적으로 함수이므로, 확장 프로퍼티를 만들 수도 있습니다.

val Context.preferences: SharedPreferences
    get() = PreferenceManager
        .getDefaultSharedPreferences(this)

 

코드에서 확인할 수 있는 것처럼 프로퍼티는 필드에 접근할 수 있는 접근자를 나타낼 뿐입니다.

이처럼 프로퍼티를 함수 대신 사용할 수도 있지만, 이런 함수에 알고리즘의 동작을 나타내는 것은 좋지 않습니다.

// 가능은 하지만, 이렇게 쓰지 마세요!
val Tree<Int>.sum: Int
    get() = when(this){
        is Leaf -> value
        is Node -> left.sum + right.sum
    }

 

이러한 처리는 프로퍼티가 아니라 함수로 구현해야 합니다.

fun Tree<Int>.sum(): Int = when (this) {
    is Leaf -> values
    is Node -> left.sum() + right.sum()
}

 

원칙적으로 프로퍼티는 상태를 나타내거나 설정하기 위한 목적으로만 사용하는 것이 좋고, 다른 로직 등을 포함하지 않아야 합니다.

이러한 프로퍼티는 여러가지 오해와 잘못된 사용을 일으킬 수 있습니다.

구체적으로 프로퍼티 대신 함수를 사용하는 것이 좋은 경우를 정리해 보면, 다음과 같습니다.

 

  • 비즈니스 로직을 포함하는 경우: 프로퍼티에 비즈니스 로직이 포함되어있으면, 객체가 어떻게 동작할지 예측하기 어렵습니다.
  • 결정적이지 않은 경우: get의 멱등성이 깨질 수 있습니다. 함수를 여러번 호출하게되면, 반환값이 달라질 수 있습니다.
  • 변환의 경우: 타입 변환의 경우에도, Int.toDouble() 같은 함수를 사용하지, 프로퍼티에서 변환을 처리할거라 예상하기 어렵습니다.
  • 연산 비용이 높거나, 복잡도가 O(1)보다 큰 경우: 보통 프로퍼티getter, setter가 연산비용이 높다고 생각하지 않습니다. 이는 개발자의 최적화 대상에서 빠지게 됩니다.
  • 게터에서 프로퍼티의 상태 변경이 일어나야 하는 경우: 관습적으로 getter, setter가 객체의 상태를 변화시킬거라고 생각하지 않습니다. 따라서 게터에서 프로퍼티의 상태 변화를 일으킨다면, 함수를 사용하는 것이 좋습니다.

 

많은 사람은 경험적으로, 프로퍼티는 상태 집합을 나타내고, 함수는 행동을 나타낸다고 생각합니다.

반응형

'Kotlin > 이펙티브 코틀린' 카테고리의 다른 글

[이펙티브 코틀린] Item17. 이름 있는 아규먼트를 사용하라  (0) 2023.10.29
[이펙티브 코틀린] Item15. 리시버를 명시적으로 참조하라  (0) 2023.10.01
[이펙티브 코틀린] Item14. 변수 타입이 명확하지 않은 경우 확실하게 지정하라  (0) 2023.10.01
'Kotlin/이펙티브 코틀린' 카테고리의 다른 글
  • [이펙티브 코틀린] Item18. 코딩 컨벤션을 지켜라
  • [이펙티브 코틀린] Item17. 이름 있는 아규먼트를 사용하라
  • [이펙티브 코틀린] Item15. 리시버를 명시적으로 참조하라
  • [이펙티브 코틀린] Item14. 변수 타입이 명확하지 않은 경우 확실하게 지정하라
어리둥절범고래
어리둥절범고래
    반응형
  • 어리둥절범고래
    안드로이드 개발자 노트
    어리둥절범고래
  • 전체
    오늘
    어제
    • 분류 전체보기 (111)
      • Android (14)
      • Kotlin (94)
        • 이펙티브 코틀린 (52)
        • 코틀린 완벽 가이드 (14)
        • 코틀린 코루틴 (17)
        • 코루틴의 정석 (11)
      • Conference (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
어리둥절범고래
[이펙티브 코틀린] Item16. 프로퍼티는 동작이 아니라 상태를 나타내야 한다.
상단으로

티스토리툴바