코드 다듬기 : 로컬 함수와 확장

Suyong Lee·2021년 9월 14일
0

Kotlin

목록 보기
15/16
post-thumbnail

코틀린에서는 함수에서 추출한 함수를 원 함수 내부에 중첩시킬 수 있다.

그렇게 하면 문법적인 부가 비용을 들이지 않고도 깔끔하게 코드를 조직할 수 있다.

이것은 흔히 발생하는 코드 중복을 로컬 함수를 통해 제거하는 방법을 익히기 위해 필요하다.

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") // 필드 검증 ( 혹시 유저 주소가 비어있는지)
    }
    
    // user를 데이터베이스에 저장한다.
}

-> saveUser(User(1, "", ""))
java.lang.IllegalArgumentException: Can't save user 1: empty Name

위 코드는 코드 중복이 그리 많진 않지만, 유저 필드를 검증할 때 필요한 여러 경우를 하나씩 처리하는 메서드로 넘쳐나면 그리 생산적인 코드라고 볼 수 없다.

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

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") // 로컬 함수를 호출해서 각 필드를 검증
    
    // user를 데이터베이스에 저장한다.
}

검증 로직 중복은 사라졌고, 필요하면 User의 다른 필드에 대한 검증도 쉽게 추가할 수 있다. (User에 파라미터만 더 추가하면 되니까)

로컬 함수는 자신이 속한 바깥 함수의 모든 파라미터와 변수를 사용할 수 있다.

이런 성질을 이용해 불필요한 User 파라미터를 없애보자.

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

fun saveUser(user: User) {
	fun validate(value: String, fieldName: String) { // 이제 saveUser 함수의 user 파라미터를 중복 사용하지 않는다.
    	if(value.isEmpty()) {
        	throw IllegalArgumentException(
            	"Can't save user ${user.id} : " +   // 바깥 함수의 파라미터에 직접 접근 가능
                	+ "empty $fieldName")
        }
    }
    
    validate(user.name, "Name")
    validate(user.address, "Address")
    
    // user를 데이터베이스에 저장한다.
}

아래는 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") // User의 프로퍼티 직접 사용 가능
        }
    }
    
    validate(name, "Name")
    validate(address, "Address")
}

fun saveuser(user: User) {     // 확장 함수 호출
	user.validateBeforeSave()
    
    // user를 데이터베이스에 저장한다.
}

확장 함수를 로컬 함수로 정의할 수도 있다. 즉 User.validateBeforeSave를 saveUser 내부에 로컬 함수로 넣을 수 있다.

하지만 중첩된 함수의 깊이가 깊어지면 코드 가독성이 상당히 떨어진다.

일반적으로는 한 단계만 함수를 중첩시키는 방법을 권장한다고 한다.

profile
이수용

0개의 댓글

관련 채용 정보