[Kotlin in Action] 12. 로컬함수 확장 (코드 리팩토링, 로컬함수, 중첩함수)

반응형
728x90
반응형

로컬함수 확장

예제 : 데이터베이스에 사용자 객체를 저장하기전, 각 필드의 validation 을 검증하는 로직

 

1단계. 코드 중복을 보여주는 예제

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

fun saveUser(user: User) {
    if (user.name.isEmpty()) {
        throw IllegalArgumentException (
            "Can't save User ${user.id}: empty Name"
        )
    }

    if (user.address.isEmpty()) {
        throw IllegalArgumentException (
            "Can't save User ${user.id}: empty Address"
        )
    }
}

fun main() {
    saveUser(User(1, "", ""))
}

 

 

2단계. 코드 중복 줄이기

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

fun saveUser2(user: User) {
    // 한 필드를 검증하는 로컬 함수를 정의한다.
    fun validate(user: User,
                 value: String,
                 fieldName: String) {
        if (value.isEmpty()) {
            throw IllegalArgumentException (
                "Can't save user ${user.id}: empty $fieldName"
            )
        }
    }

    // 로컬함수를 호출해서 각 필드를 검증한다.
    /* 아쉬운점 : 로컬함수에게 User 객체를 하나하나 전달해야한다. */
    validate(user, user.name, "Name")
    validate(user, user.address, "Address")
}

 

2-1) 로컬 함수를 정의했다.

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

 

2-2) 로컬 함수를 호출한다.

validate(user, user.name, "Name")
validate(user, user.address, "Address")

 

문제점

로컬함수에게 User 객체를 하나하나 중복으로 전달해줘야한다.

 

 

3단계. 로컬함수에서 바깥 함수의 파라미터 접근하기

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

fun saveUser3(user: User) {
    fun validate(value: String,
                 fieldName: String) {
        if (value.isEmpty()) {
            throw IllegalArgumentException (
                // 바깥 함수의 user 파라미터에 직접 접근할 수 있다.
                "Can't save user ${user.id}: empty $fieldName"
            )
        }
    }

    validate(user.name, "Name")
    validate(user.address, "Address")
}

 

3-1) 바깥 함수 saveUser3의 user 파라미터에 직접 접근할 수 있다.

fun validate(value: String,
             fieldName: String) {
    if (value.isEmpty()) {
        throw IllegalArgumentException (
            // 바깥 함수의 user 파라미터에 직접 접근할 수 있다.
            "Can't save user ${user.id}: empty $fieldName"
        )
    }
}

 

 

4단계. 검증 로직을 확장 함수로 추출하기

  • 함수 중첩은 한단계만 권장한다. 중첩된 함수의 깊이가 깊어지면 코드를 읽기가 어려워진다.
class User_4(val id: Int, val name: String, val address: String)

fun User.validateBeforeSave() {
    fun validate(value:String, fieldName:String) {
        if (value.isEmpty()) {
            throw IllegalArgumentException (
                // User의 프로퍼티를 직접 사용할 수 있다.
                "Can't save user ${id}: empty $fieldName"
            )
        }
    }

    validate(name,"Name")
    validate(address,"Address")
}

fun saveUser4(user: User) {
    // 확장함수를 호출한다.
    user.validateBeforeSave()
}

 

4-1) User의 프로퍼티를 직접 사용한다.

fun User.validateBeforeSave() {
    fun validate(value: String, fieldName: String) {
        if (value.isEmpty()) {
            throw IllegalArgumentException (
                // User의 프로퍼티를 직접 사용할 수 있다.
                "Can't save user ${id}: empty $fieldName"
            )
        }
    }
    
    ...
}

 

4-2) 확장 함수를 호출한다.

fun saveUser4(user: User) {
    // 확장함수를 호출한다.
    user.validateBeforeSave()
}

 

반응형

Designed by JB FACTORY