다음과 같이 숫자를 특정 범위에 맞추는 알고리즘은 stdlib의 coerceIn 확장함수로 이미 구현되어 있다.
val percent = when {
numberFromUser > 100 -> 100
numberFromUser < 0 -> 0
else numberFromUser
}
// 이미 존재
val percent = numberFromUser.coerceIn(0, 100)
이미 있는 것을 활용하면 코드가 짧아진다는 것 이외에도 다양한 장점이 있다.
- 코드 작성 속도가 빨라진다.
- 구현을 따로 읽지 않아도 함수의 이름 등만 보고도 무엇을 하는지 확실하게 알 수 있다.
- 직접 구현할 때 발생할 수 있는 실수를 줄일 수 있다.
- 제작자들이 한번만 최적화하면, 모든 곳이 최적화의 혜택을 받을 수 있다.
표준 라이브러리 살펴보기
일반적인 알고리즘은 대부분 정의되어 있다.
override fun saveCallResult(item: SourceResponse) {
var sourceList = ArrayList<SourceEntity>()
item.sources.forEach {
var sourceEntity = SourceEntity()
sourceEntity.id = it.id
sourceEntity.category = it.category
sourceEntity.country = it.country
}
db.insertSources(sourceList)
}
어떤 자료형을 다른 자료형으로 매핑하기 위한 map과 같은 함수는 이미 제공되고 있으니, 위의 코드에서도 forEach를 사용하지 말고 map을 사용하는 것이 좋다.
override fun saveCallResult(item: SourceResponse) {
val sourceEntries = item.sources.map(::sourceToEntry)
db.insertSources(sourceList)
}
나만의 유틸리티 구현하기
상황에 따라 표준 라이브러리에 없는 알고리즘이 필요할 수도 있다.
컬렉션에 있는 모든 숫자의 곱을 계산하는 라이브러리가 필요한 경우 범용 유틸리티 함수(universal utility function)로 정의하는것이 좋다.
fun Iterable<Int>.product() = fold(1) { acc, i -> acc * i }
필요 없는 함수를 중복해서 만들지 않게 기존에 관련된 함수가 있는지 탐색하는 과정이 필요하다.
많이 사용되는 알고리즘을 추출하는 방법으로는 톱레벨 함수, 프로퍼티 위임, 클래스 등이 있다.
확장 함수는 앞의 방법과 비교해 다음과 같은 장점을 가지고 있다.
- 함수는 상태를 유지하지 않으므로, 행위를 나타내기 좋다.
- 톱레벨 함수와 비교해서, 확장 함수는 구체적인 타입이 있는 객체에만 사용을 제한할 수 있으므로 좋다.
- 수정할 객체를 아규먼트로 전달받아 사용하는 것보다는 확장 리시버로 사용하는 것이 가독성 측면에서 좋다.
- 확장 함수는 객체에 정의한 함수보다 객체를 사용할 때, 자동 완성 기능 등으로 제안이 이루어지므로 쉽게 찾을 수 있다.
정리
- 일반적인 알고리즘을 반복해서 만들지 말자. 대부분 stdlib에 이미 정의되어 있다.
Reference
- 이펙티브 코틀린 - 프로그래밍 인사이트, 마르친 모스칼라 지음, 윤인성 옮김
개인적인 기록을 위해 작성된 글이라 잘못된 내용이 있을 수 있습니다.
오류가 있다면 댓글을 남겨주세요.