클린코드(2)-함수

김재원·2022년 2월 7일
0

클린코드

목록 보기
2/2
post-thumbnail

이전글에서는 이름을 정하는 방법에 대해 알아보았습니다. 그리고 이 글에선 함수를 어떻게 만들어야하는지에 대해 알아보겠습니다.

함수는 작게 만들어라

함수는 작을수록 좋습니다. 함수를 작게 만드는 방법은 다음과 같습니다.

  • if문, else문, while문 등에 들어가는 블록은 한줄이어야합니다.
  • 함수의 들여쓰기 수준은 1단이나 2단을 넘어서면 안 됩니다.

이런 방법들을 사용해 함수를 작게 만들면 읽고 이해하기가 훨씬 쉬워집니다.

한 가지만 해라

함수는 한 가지만 해야합니다. 여기서 이 한 가지를 정하기 힘들 수 있습니다. '한 가지'를 판단하기 어렵다면 함수가 의미 있는 이름으로 다른 함수를 추출할 수 있는지 찾아보면 됩니다. 다른 함수로 추출할 수 있다면 그 함수는 한 가지만을 하고있는것이 아닙니다.

fun calculate(parameter: Int): Int {
   val result = parameter
   result += 1
   result *= 13
   return result
}

위 함수는 1을 더하고 13을 더하는 2가지 역할을 맡고있습니다.

fun calculate(paramter: Int): Int {
   var result = parameter
   result = plusOne(result)
   result = multiplyThirteen(result)
   return result
}

fun plusOne(value: Int): Int =
   value + 1
   
fun multiplyThirteen(value: Int): Int =
   value * 13

이런식으로 한가지만 할 수 있도록 만들어줘야합니다.

함수 당 추상화 수준은 하나로

한 함수안에 추상화 수준을 섞으면 코드를 읽는 사람이 헷갈립니다. 특정 표현이 근본 개념인지 아니면 세부사항인지 구분하기 어렵기 때문입니다.

fun LocalDateTime.toRemainTimeText(): String {
    val timeDifferent = DifferentTime(this)

    val minuteDifferent = timeDifferent.minute
    var text = if (minuteDifferent <= 0) "지난시간: " else "남은시간: "
    val addText = when(text) { 
    year > 0 -> "${year}년 "
    month > 0 -> "${month}개월 "
    day > 0 -> "${day}일 "
    hour > 0 -> "${hour}시간 "
    minute > 0 -> "${minute}분"

    year < 0 -> "${year*-1}년 "
    month < 0 -> "${month*-1}개월 "
    day < 0 -> "${day*-1}일 "
    hour < 0 -> "${hour*-1}시간 "
    minute < 0 -> "${minute*-1}분"

    else -> "0분"
    }
    text += addText
    return text
}

위 코드에서 addText는 시간에 따라서 년도를 붙일지, 개월을 붙일지를 판단하는것은 세부적인 사항입니다. 따라서 다른 함수로 묶어서 추상화 수준을 통일해야합니다.

fun LocalDateTime.toRemainTimeText(): String {
    val timeDifferent = DifferentTime(this)

    val minuteDifferent = timeDifferent.minute
    var text = if (minuteDifferent <= 0) "지난시간: " else "남은시간: "
    text += timeDifferent.toRemainShowText()
    return text
}

fun DifferentTime.toRemainShowText(): String = when {
    year > 0 -> "${year}년 "
    month > 0 -> "${month}개월 "
    day > 0 -> "${day}일 "
    hour > 0 -> "${hour}시간 "
    minute > 0 -> "${minute}분"

    year < 0 -> "${year*-1}년 "
    month < 0 -> "${month*-1}개월 "
    day < 0 -> "${day*-1}일 "
    hour < 0 -> "${hour*-1}시간 "
    minute < 0 -> "${minute*-1}분"

    else -> "0분"
}

서술적인 이름을 사용하라

함수 이름만 읽고 그 함수가 하는 역활을 알 수 있다면 성공한 이름입니다. 이름이 길어도 좋으므로 서술적인 이름을 사용해야합니다.

fun fetch() {
    viewModelScope.launch {
    	repository.fetch()
    }
}

위 함수이름만 읽고는 도저히 어떤 값을 가져오는지 알 수 없습니다.
따라서 아래와 같이 바꿔줘야합니다.

fun fetchTodoList() {
   viewModelScope.launch {
   	repository.fetchTodoList()
   }
}

함수 인수

함수에서 이상적인 인수 개수는 0개입니다. 인수의 개수는 적을 수록 좋습니다. 그리고, 만약 함수에 인수를 넣는다면 함수이름에 인수이름을 넣는것도 좋은 방법입니다.

부수 효과를 일으키지 마라

한가지만 하겠다고 만든 함수에서 다른것도 한다면 부수효과를 일으키는것입니다. 함수를 사용하는 입장에서 혼란을 줄이려면 부수효과를 일으키지 말아야합니다.

fun fetchTodoList() {
   resetUserInformation()
   repository.fetchTodoList()
}

위 예시에선 fetchTodoList에서 사용자 정보를 초기화 하고 값을 불러오고 있습니다. 이런 부수효과를 일으킬 수 있는 코드는 바람직하지 않습니다.

명령과 조회를 분리하라

함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야한다

예를들어 사용자 정보를 저장하는 함수가 있다고 가정했을때, 그 함수가 동시에 사용자의 나이를 반환한다고 생각해봅시다. 그럼 그 코드는 아래와 같을것입니다.

fun saveUser(user: User): Int {
   repository.saveUser(user)
   return user.age
}

fun main() {
   val user = User("김재원",19)
   val userAge = saveUser(user)
}

위 코드는 함수는 한가지만 해야한다는 규칙을 위반할 뿐더러 사용자 나이를 가져오는데 사용자를 저장하는 함수를 사용하는 괴리감이 드는 코드가 됩니다. 따라서 명령과 조회는 꼭 분리해야합니다.

오류 코드보다 예외를 사용하라

위에서 정리했던 명령과 조회의 연장선에있는 규칙입니다. 뭔가를 명령하는 함수가 오류코드를 반환한다면 오류코드를 조회하는 함수가 되기 때문입니다. 따라서 예외를 사용해야합니다.

반복하지 마라

중복코드는 코드의 길이를 늘릴뿐더러, 알고리즘의 변화가 있으면 해당하는 코드들을 다 바꿔야한다는 점에서 문제가 됩니다. 중복되는 코드들을 추상 클래스로 묶거나, 새로운 함수를 만듦으로써 해결할 수 있습니다.

이렇게 해서 함수를 만드는 규칙에 대해서 알아보았습니다. 규칙들을 잘 지켜서 개발을 한다면 더욱 편한 개발일 될 수 있을것입니다. 그럼 즐거운 개발 되세요😌.

참고
로버트 C 마틴, ⌜클린코드⌟, 박재호 이해영옮김, 40 ~ 65쪽

profile
항상 배울 것을 찾는 개발자입니다🔥.

0개의 댓글