새소식

Language/Kotlin

[Effective Kotlin] 2장 가독성 : Item 15번 ~ 18번

  • -

 

✅ 리시버가 명확하지 않다면, 명시적으로 리시버를 적어서 명확하게 하는 것이 좋다. 

  • 레이블 없이 리시버를 사용하면 가장 가까운 리시버
  • 외부에 있는 리시버를 사용하려면, 레이블을 사용 

예시)

apply 스코프 내부에 레이블 없이 사용된 리시버는 create()메서드의 반환값을 가리킨다. (this?.name)
그리고 외부 리시버를 사용하기 위해 레이블을 붙여 사용하는 것을 확인할 수 있다. (this@Node.name)

 

출력: Created parent-child in  parent

 

 

어떤 리시버를 활용하는지 의미를 명확하게 하면 코드의 안정성과 가독성을 향상할 수 있다.
리시버를 제거하여 코드를 짧게 만든다고 해서 무조건 좋은 것은 아니다. 

 

 

 

 

✅ 코틀린 프로퍼티 

  • 코틀린의 프로퍼티와 자바의 필드는 데이터를 저장한다는 점은 동일하다. 
  • 코틀린 프로퍼티는 사용자 정의 세터와 게터를 가질 수 있다. 
  • 코틀린 프로퍼티는 접근자로 val 은 get( )만 / var 은 get(), set() 모두 정의할 수 있다. 
  • 프로퍼티는 필드가 필요 없다. 프로퍼티는 개념적으로 접근자를 나타낸다.
    그러므로 인터페이스에서도 프로퍼티를 정의할 수 있다. 

 

 

✅ 코틀린 프로퍼티 get( ), set() 예시 

class User {
    var name: String = "unknown"
        get() = field.capitalize() // 게터 정의: 이름을 대문자로 변환하여 반환
        set(value) {
            field = value.trim() // 세터 정의: 입력받은 값을 공백 제거 후 저장
        }
    
    var age: Int = 0
        get() = field
        set(value) {
            if (value >= 0) field = value // 세터 정의: 나이는 0 이상이어야 한다.
        }
}

fun main() {
    val user = User()
    
    user.name = "   john doe   " // 세터를 통해 공백이 제거되고 저장됨
    println(user.name) // 출력: "John Doe" (게터를 통해 대문자로 변환되어 출력됨)
    
    user.age = -5 // 나이에 음수를 설정하려고 하면 세터에서 거부됨
    println(user.age) // 출력: 0 (음수가 설정되지 않음)
    
    user.age = 30 // 올바른 나이 설정
    println(user.age) // 출력: 30
}

 

 

위 예시처럼 코틀린 프로퍼티는 디폴트로 캡슐화되어 있습니다. 
만약 User 클래스의 age가 프로젝트 전역에서 사용되고 있다고 가정하겠습니다. age의 타입을 String으로 변경해야 한다면 사용되는 모든 곳을 직접 변경하지 않고 게터와 세터를 변경해서 해결할 수 있습니다. 
    // Int 타입의 값을 받아서 String으로 변환하여 저장하는 setter
    var ageAsInt: Int
        get() = age.toIntOrNull() ?: 0 // String을 Int로 변환하여 반환. 변환 실패 시 0 반환
        set(value) {
            age = value.toString() // Int 값을 String으로 변환하여 age에 저장
        }

 

 

 

✅ 코틀린 프로퍼티 위임 

Kotlin에서 프로퍼티 위임(Property Delegation)은 특정 작업을 다른 클래스에 위임함으로써 코드를 더 깔끔하게 관리할 수 있게 해 줍니다. 

import kotlin.reflect.KProperty

class DelegatedIntProperty(var value: Int) {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
        println("${property.name} 프로퍼티의 get() 호출")
        return value
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: Int) {
        println("${property.name} 프로퍼티의 set() 호출, 새로운 값: $newValue")
        value = newValue
    }
}

class User {
    var age: Int by DelegatedIntProperty(0)
}

fun main() {
    val user = User()
    
    println(user.age)  // age 프로퍼티의 get() 호출
    user.age = 25      // age 프로퍼티의 set() 호출, 새로운 값: 25
    println(user.age)  // age 프로퍼티의 get() 호출
}

 

 

✅  프로퍼티의 게터, 세터에 알고리즘 동작을 넣는 것은 좋지 않다. 

게터와 세터에 when, if 등 여러 분기 또는 알고리즘 동작을 하는 로직은 해당 프로퍼티에 대한 오해를 발생시킬 가능성이 높아집니다. 
또한 만약 크기가 큰 컬렉션에 데이터를 읽고 쓸 경우 더 많은 계산을 필요로 하게 됩니다.
즉, 이러한 로직은 함수로 구현하는 것이 바람직합니다. 

 

프로퍼티 게터. 세터 대신 함수를 사용하기 좋은 경우 

  • 연산 비용이 높거나, 복잡도가 O(1)보다 큰 경우
    관습적으로 프로퍼티를 사 용할 때 연산 비용이 많이 필요하다고 생각하지 않습니다. 연산 비용이 많 이 들어간다면, 함수를 사용하는 것이 좋습니다. 그래야 사용자가 연산 비 용을 예측하기 쉽고, 이를 기반으로 캐싱 등을 고려할 수 있기 때문입니다.
  • 비즈니스 로직(애플리케이션의 동작)을 포함하는 경우
    관습적으로 코드를 읽을 때 프로퍼티가 로깅, 리스너 통지, 바인드된 요소 변경과 같은 단순한 동작 이상을 할 거라고 기대하지 않습니다.
  • 결정적이지 않은 경우
    같은 동작을 연속적으로 두 번 했는데 다른 값이 나 올 수 있다면, 함수를 사용하는 것이 좋습니다.
  • 변환의 경우
    변환은 관습적으로 Int.toDouble()과 같은 변환 함수로 이루어집니다. 따라서 이러한 변환을 프로퍼티로 만들면, 오해를 불러일으킬 수 있습니다.
  • 게터에서 프로피디의 상태 변경이 일어나야 하는 경우
    관습적으로 게터에서 프로퍼티의 상태 변화를 일으킨다고 생각하지는 않습니다. 따라서 게터에서 프로퍼티의 상태 변화를 일으킨다면, 함수를 사용하는 것이 좋습니다.

 

 

 

✅  아규먼트의 의미를 명확하게 하는 것이 좋다. 

예시) 이름이 없는 아규먼트

fun introduce(name: String, age: Int, city: String) {
    println("이름: $name, 나이: $age, 도시: $city")
}

fun main() {
    introduce("홍길동", 25, "서울")
}

 

 

예시) 이름이 있는 아규먼트

fun introduce(name: String, age: Int, city: String) {
    println("이름: $name, 나이: $age, 도시: $city")
}

fun main() {
    // 이름있는 아규먼트를 사용하여, 파라미터의 순서와 상관없이 값을 전달
    introduce(age = 25, name = "홍길동", city = "서울")
}

 

 

 

✅  아규먼트는 언제 사용해야 할까?

이름 있는 아규먼트를 사용하면 코드가 길어지지만, 두 가지 장점이 있습니다. 

  • 이름을 기반으로 값이 무엇을 나타내는지 알 수 있다. 
  • 파라미터 입력 순서와 상관없으므로 안전하다.

 

이름 있는 아규먼트는 개발자가 코드를 읽을 때도 편리하게 활용되며, 코드의 안전성도 향상할 수 있습니다. 

 

 

 

✅  코틀린 코딩 컨벤션

코틀린 문서를 보면 알 수 있듯이, 코틀린은 굉장히 잘 정리된 코딩 컨벤션을 가지고 있습니다.
모든 프로젝트에 최적은 아닐 수 있습니다. 
그러나 컨벤션을 지키면 다음과 같은 이점을 얻을 수 있습니다. 

  • 어떤 프로젝트를 접해도 쉽게 이해할 수 있다. 
  • 다른 외부 개발자도 프로젝트의 코드를 쉽게 이해할 수 있다. 
  • 다른 개발자도 코드의 작동 방식을 쉽게 예측할 수 있다. 
  • 코드를 병합하고, 한 프로젝트의 코드 일부를 다른 코드로 이동하는 것이 쉽다. 

 

✅  컨벤션 도구 

  • IntelliJ 포매터
  • Ktlint 
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.