언어/Kotlin

[Kotlin In Action] 로컬 함수와 확장

원석💎-dev 2022. 4. 25. 00:07
반응형

 흔히 발생하는 중복코드를 로컬 함수를 통해 어떻게 제거할 수 있는지 살펴보자.

 

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 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")
    
    // 사용자 저장
    .
    .
    .
}

 위 코드로 중복이 어느정도 제거되었다. 하지만 ${user.id}를 위해 user를 파라미터로 넘겨야하는 하는 점이 아쉽다. 다행이도 로컬 함수는 자신이 속한 바깥 함수의 모든 파라미터와 변수를 사용할 수 있다.

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")
    
    // 사용자 저장
    .
    .
    .
}

2. 확장 함수

위의 코드에 확장 함수를 사용하여 검증로직을 더 깔끔하게 뽑아낼 수 있다.

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

fun User.validateBeforeSave() {
    fun validate(
      value: String,
      fieldName: String,
    ) {
      if (value.isEmpty()) {
        // user의 프로퍼티 직접 사용가능
        throw IllegalArgumentException("Can't save user ${this.id}: empty $fieldName")
      }
    }
    
    validate(user.name, "Name")
    validate(user.address, "Address")
}

fun saveUser(user: User) {
    
    User.validateBeforeSave() // 확장함수 호출
    
    // 사용자 저장
    .
    .
    .
}

3. 좀 더 나아가서 (require)

[require 블록]을 사용하면 조금 더 가독성 좋은 코드를 작성 할 수 있다.

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

fun User.validateBeforeSave() {
    fun validate(
      value: String,
      fieldName: String,
    ) {
      require(value.isEmpty()) { "Can't save user ${this.id}: empty $fieldName" }
    }
    
    validate(user.name, "Name")
    validate(user.address, "Address")
}

fun saveUser(user: User) {
    
    User.validateBeforeSave() // 확장함수 호출
    
    // 사용자 저장
    .
    .
    .
}
반응형