[코틀린] 로컬함수

hee09·2021년 11월 16일
0
post-thumbnail
post-custom-banner

참조
Kotlin in Action을 보고 작성하였습니다.

로컬함수

자바에서 반복되는 코드를 줄이기 위해 많은 경우 메서드 추출 리팩토링을 적용해서 긴 메서드를 부분부분 나눠서 각 부분을 재활용 할 수 있습니다. 하지만 그렇게 코드를 리팩토링하면 클래스 안에 작은 메서드가 많아지고 각 메서드 사이의 관계를 파악하기 힘들어서 코드를 이해하기 더 어려워 질 수 있습니다.

코틀린에서는 함수에서 추출한 함수를 원 함수 내부에 중첩시킬 수 있습니다. 그렇게 되면 문법적인 부가 비용을 들이지 않고도 깔끔하게 코드를 조작할 수 있습니다.

예제를 보며 확인하겠습니다.


중복 예시

사용자를 데이터베이스에 저장하는 함수가 있습니다. 이때 데이터베이스에 사용자 객체를 저장하기 전에 각 필드를 검증하는 코드입니다.

data class User(val name: String, val address: String)

fun saveUser(user: User) {
    // 필드 검증이 중복
    if(user.name.isEmpty()) {
        throw IllegalArgumentException(
            "${user.id} : empty Name"
        )
    }

    // 필드 검증이 중복
    if(user.address.isEmpty()) {
        throw IllegalArgumentException(
            "${user.id} : empty Address"
        )
    }

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

위 예시에서 만약 사용자의 필드가 늘어난다면 검증하는 코드가 계속해서 증가합니다.

이런 경우 검증 코드를 로컬 함수로 분리하면 중복을 없애는 동시에 코드 구조를 깔끔하게 유지할 수 있습니다.


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

fun saveUser(user: User) {
    // 로컬 함수 정의
    fun validate(user: User, value: String, fieldName: String) {
        if(value.isEmpty()) {
            throw IllegalArgumentException(
                "${user.id} : empty $fieldName"
            )
        }
    }
     
    // 로컬 함수를 호출해서 각 필드를 검증
    validate(user, user.name, "Name")
    validate(user, user.address, "Address")

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

검증 로직 중복이 사라졌고, 필요하면 User의 다른 필드에 대한 검증도 쉽게 추가할 수 있습니다. 다만 로컬 함수는 자신이 속한 바깥 함수의 모든 파라미터와 변수를 사용할 수 있습니다.


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

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

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

validate 메서드 안에서 중복해서 user 파라미터를 사용하지 않고 바깥 메서드인 saveUser의 파라미터에 직접 접근하여 사용합니다.


검증 로직을 확장 함수로 만들어 개선

// 검증 로직을 확장 함수로 추출
fun User.validateBeforeSave() {
    fun validate(value: String, fieldName: String) {
        if(value.isEmpty()) {
            throw IllegalArgumentException(
                // User 클래스의 프로퍼티에 직접 접근
                "$id : empty $fieldName"
            )
        }
    }

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

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

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

검증 로직은 User를 사용하는 다른 곳에서는 쓰이지 않는 기능이기 때문에 User에 포함시키지 않고 확장 함수로 만든 코드입니다. 이와 같이 작성하면 User를 간결하게 유지하여 더 쉽게 코드를 파악할 수 있습니다.

한 객체만을 다루면서 객체의 비공개 데이터를 다룰 필요는 없는 함수(확장 함수는 비공개 멤버에 접근하지 못함)는 위와 같이 확장 함수로 만들면 객체.멤버처럼 수신 객체를 지정하지 않고도 공개된 멤버 프로퍼티나 메서드에 접근할 수 있습니다.

profile
되새기기 위해 기록
post-custom-banner

0개의 댓글