본문 바로가기
Android & Kotlin/Kotlin

[Kotlin local function] 코틀린 로컬함수와 확장

by 말린밴댕이_공부 2024. 5. 2.
반응형

코틀린 로컬함수

함수에서 추출한 함수를 원 함수 내부에 중첩 시켜 코드를 깔끔하게 조직할 수 있음

 

왜 사용하지?

정보 제거의 중요성 DRY(Don’t Repeat Yourself) 원칙을 지키기 위해!

 

로컬함수 예시

다른 함수 내부의 정의된 함수를 의미하며 즉, 함수안에 다른 함수를 선언

fun calRectArea(length: Int, width: Int): Int {
    fun multiply(a: Int, b: Int): Int {
        return a * b
    }

    return multiply(length, width)
}

 

위와 같이 함수 안에 다른 함수를 선언하는 것을 말한다. (역시 백문이불여일견)

 

그럼 로컬함수를 통해 코드중복을 제거하는 예시를 보자

1. 코드 중복

data 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.id} : empty Name"
        )
    }

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

    // user를 데이터베이스에 저장하는 코드
}

 

위 코드는 코드 중복이 많지 않지만 클래스에 대하여 사용자의 필드를 검증할 때 필요한 여러 경우를 하나씩 처리하는 메서드로 넘쳐나는 것은 옳지 않다.

 

만약 name, address, age, phone, height, weight 이렇게 점점 많아 지는 것에 대하여 하나씩 검증하는 것은 상상만 해도 어질어질하다.

 

 

다음은 로컬함수로 분리하여 중복을 없애는 예시

2. 로컬 함수 → 코드 중복 줄이기

data 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(
                "Cant Save : ${user.id} : empty $fieldName"
            )
        }
    }
     
    // 로컬 함수를 호출해서 각 필드를 검증함
    validate(user, user.name, "Name")
    validate(user, user.address, "Address")

    // user를 데이터베이스에 저장하는 코드
}

 

검증 로직 중복은 사라졌고 User의 다른 필드에 대한 검증도 쉽게 추가할 수 있다.

하지만, User 객체를 로컬함수에게 하나하나 전달하는 점에서 단점이 있다.

 

 

 

아래는 그것을 보완하고자 로컬함수에서 바깥 함수의 파라미터가 접근이 가능하므로 불필요한 User 파라미터를 삭제한다.

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

fun saveUser(user: User) {
    fun validate(value: String, fieldName: String) {
        if(value.isEmpty()) {
            throw IllegalArgumentException(
                // 바깥 함수 파라미터에 직접 접근
                "Cant Save : {user.id} : empty $fieldName"
            )
        }
    }
     
    // 로컬 함수를 호출해서 각 필드를 검증
    validate(user.name, "Name")
    validate(user.address, "Address")

    // user를 데이터베이스에 저장하는 코드
}

불필요한 User 파라미터를 삭제하였다.

이는 바깥 함수의 모든 파라미터와 변수를 사용 가능하다는 점을 이용하여 진행한 것이다.

 

 

 

이제 확장함수로 추출하여 만든 예시를 보자

4. 검증 로직을 확장 함수로 추출

data 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(
                "Cant Save : $id : empty $fieldName"
            )
        }
    }

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

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

    // user를 데이터베이스에 저장하는 코드 
}

 

 

 

 

이와 같은 검증 로직은 User를 사용하는 다른 곳에서는 쓰이지 않는 기능이기 때문에 User에 포함시키지 않고 확장함수를 이용하여 User를 간결해져서 더 쉽게 코드의 파악이 가능해진다.

 

한 객체만을 다루면서 객체를 비공개 데이터를 다룰 필요는 없는 함수는 확장함수로 만들면 객체 · 맴버 처럼 수신 객체를 지정하지 않고도 공개된 맴버 프로퍼티나 매서드에 접근할 수 있다.

 

*참고 : Kotlin In Action*

반응형

댓글