본문 바로가기
2023년 이전/kotlin

kotlin - 로컬 함수와 확장

by JeongUPark 2020. 2. 20.
반응형

[출처 -  Kotlin In Action] [아래 내용들은 Kotlin In Action을 공부하면서 스스로 정리한 내용입니다] 

 

코딩을 할때 중복을 피하라는 말이 있습니다. 하지만 중복을 피하기 위해 메소드를 너무 나누면 코드 사이의 관계 파악이 어렵고 이해하기 힘들어 유지 보수가 힘들어 집니다.  그리고 inner class로 코드 안에 넣으면 코드는 깔끔해지지만 불필요한 준비 코드가 늘어납니다.

코틀린에서는 함수에서 추출한 함수를 원 함수 내부에 중첩시킴으로써 깔끔하게 코딩을 할 수 있습니다.(이를 로컬 함수라고 합니다.) 그럼 코컬 함수를 통해 어떻게 코드가 깔 끔해지는 확인해 보겠습니다.

 

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")
    }

    // Save user to the database
}

fun main(args: Array<String>) {
    saveUser(User(1, "", ""))
}

User라는 클래스를 통하여 이름 존재 여부와 주소 존재 여부를 확인하여 둘다 존재하면 저장하고, 아닐 겨웅에는 Exception을 발생하는 코드입니다. 코드 자체는 간한다고 깔끔합니다. 그럼 로컬 함수를 사용하여 변화 시켜보겠습니다.

 

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

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

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

    // Save user to the database
}

fun main(args: Array<String>) {
    saveUser(User(1, "", ""))
}

로컬함수 validate를 사용하여 Name과 Address에 대한 체크를 하고 있습니다. 그런데 코드를 보면 validate에서 User 파라메터는 필요가 없습니다. 그러므로 저 code를 다시 고치면

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")

    // Save user to the database
}

fun main(args: Array<String>) {
    saveUser(User(1, "", ""))
}

이렇게 로컬 함수를 사용하여 검증 로직 중복이 사라진 것을 알 수 있습니다.(지금은 2개 뿐이라 별 다른 차이가 없어 보일 수 있지만, 그 갯수가 늘어날 수록 로컬 함수에 의해 코드가 더 깔끔해지는 것을 확인 할 수 있습니다.)

 

또한 User를 수신객체로하는 확장 함수를 사용하여 코드를 더 정리 할 수 도 있습니다.

 

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")
        }
    }

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

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

    // Save user to the database
}

fun main(args: Array<String>) {
   

이 확장함수로 만듬으로서 검증 로직을 가지는 확장 함수 안에서는 따로 user를 포함시키지 않아도 됩니다. 또한 User에 대해서만 이 검증로직을 쓰기 때문에 다른 곳에서 이 기능이 다른곳에 포함되지 않고 User에 대해서만 사용이 됩니다.

 

그리고 확장 함수를 로컬 함수로 정의할 수도 있지만, 중첩된 함수의 깊이가 깊어지면 코드가 읽기 힘들어 지는 문제 점이 발생합니다. 

 

반응형