[Kotlin] 함수(3)

sw·2022년 1월 4일
0

1. 가변 인자 함수

  • 파라미터 앞에 vararg를 붙이면 가변 인자를 사용할 수 있다.
  • 코틀린에서는 배열을 명시적으로 풀어서 배열의 각 원소가 인자로 전달되게 해야 한다. 배열 앞에 *를 붙이면 스프레드 연산자가 해당 작업을 진행한다.
fun listOf<T> (vararg values: T): List<T> {...}

fun main(args: Array<String>) {
    val list = listOf("args: ", *args)
    println(list)
}

2. 중위 호출(infix call)과 구조 분해 선언

  • 중위 호출 : 수신 객체와 유일한 메소드 인자 사이에 메소드 이름을 넣는다.
val map = mapOf(1 to "one", 7 to "seven")
// 중위 호출 방식으로 to라는 일반 메소드를 호출한다.

/* 아래 두 호출은 동일 */
4.to("one")
4 to "one"

// to 함수의 정의를 간략히 하면..
infix fun Any.to(other: Any): Pair(this, other)

// 구조 분해 선언으로 변수 초기화
val (number, name) = 1 to "one"

// 루프에서의 구조 분해
for((index, element) in collection.withIndex()) {
    println("$index: $element")
}


3. 로컬 함수와 확장

  • 좋은 코드의 중요한 특징 중 하나는 중복이 없는 것이다.
    DRY 원칙 : Don't Repeat Yourself
  • 많은 경우 메소드 추출 리팩토링을 적용하는데, 그렇게 하면 클래스 안에 작은 메소드가 많아지고 각 메소드 간의 관계 파악이 힘들어서 가독성이 더 떨어질 수 있다.
  • 리팩토링 진행 후에 추출한 메소드를 별도의 inner class에 넣으면 코드가 깔끔해지지만 불필요한 준비 코드가 늘어난다.

-> 코틀린에는 함수에서 추출한 함수를 원 함수 내부에 중첩시킬 수 있다.


코드 중복을 보여주는 예제

class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User){
    if(user.name.isEmpty()){...}
    
    if(user.address.isEmpty()){...}
    
    // 필드 검증이 중복된다. 필드가 늘어날수록 중복 증가
    ...
}

로컬 함수를 사용해 코드 중복 줄이기

class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User){
    
    fun validate(value: String, fieldName: String) {
        if (value.isEmpty()) {
            throw IllegalArgumentException(
                "Can't save user ${user.id}: empty $fieldName"
            )
        }
    }

    validate(user.name, "Name")
    validate(user.address, "Address")
    
    // 로컬 함수를 호출해서 각 필드를 검증한다.
    // 로컬 함수는 자신이 속한 바깥 함수의 모든 파라미터와 변수를 사용할 수 있다.
    ...
}

검증 로직을 확장 함수로 추출하기

class User(val id: Int, val name: String, val address: String)

fun User.validateBeforeSave() {
    fun validate(value: String, fieldName: String) {
        if (value.isEmpty()) {
            throw IllegalArgumentException(
                "Can't save user $id: empty $fieldName"
            )
        }
    }

    // User의 프로퍼티를 직접 사용할 수 있다.
    validate(name, "Name")
    validate(address, "Address")
}

fun saveUser(user: User){
    user.validateBeforeSave()
    ...
}

-> 검증 로직은 User를 사용하는 다른 곳에는 쓰이지 않기 때문에 User에 포함시키지 않는다. User를 간결하게 유지하면 생각해야할 내용이 줄어들어 가독성이 좋아진다.

-> 확장 함수를 로컬 함수로 정의할 수도 있다. 즉, User.validateBeforeSavesaveUser() 내부에 넣을 수 있지만 중첩된 함수의 깊이가 깊어지면 가독성이 떨어진다. 일반적으로 한 단계만 함수를 중첩시키라고 권장한다.

profile
끄적끄적

0개의 댓글