Study/이펙티브코틀린

Item 11. 가독성을 목표로 설계하라

주지민 2022. 3. 15. 01:53
반응형

본 포스트는 사내 스터디를 진행하며 정리한 포스트입니다

https://book.interpark.com/product/BookDisplay.do?_method=Detail&sc.shopNo=0000400000&dispNo=&sc.prdNo=354452929&sc.saNo=002001023&bkid1=category&bkid2=ct028023&bkid3=c1&bkid4=001

 

싸니까 믿으니까 인터파크도서

실제 개발 사례를 통해 알려주는 코드 품질 향상 전략 이 책은 더 나은 코틀린 개발자가 될 수 있도록 도움을 주는 안내서입니다. 코틀린에 어떤 기능이 있는지, 어떤 표준 라이브러리가 있는지

book.interpark.com


1. 코드 예시

  • 개발자는 어떤 코드를 작성하는데 보다 읽는데 시간을 많이 소모한다
  • 따라서 프로그래밍은 가독성을 생각하면서 코드를 작성해야한다
// A
if (person != null && person.isAdult) {
	view.showPerson(person)
} else {
	view.showError()
}


// B
person?.takeIf { it.isAdult }
    ?.let(view.showPerson)
    ?: view.showError()
  • 가독성이란 코드를 얼마큼 빠르게 이해할 수 있는지를 의미
  • A 코드는 일반적으로 널리 쓰는 관용구 (if, else...) 
  • B 코드는 코틀린에서 널리 쓰이는 관용구 ( 안전호출?. , takeIf, let 등 ), 숙달된 코틀린 개발자에게만 쉬운 코드

 

2. A 코드를 사용해야하는 이유

  • 사용 빈도가 적은 관용구는 코드를 복잡하게 만든다 
  • 그런 관용구들을 한 문장 내부에 조합해서 사용하면 복잡성은 훨씬 더 빠르게 증가
  • 수정사항이 있을 때
    • A코드에서는 if 블록에 코드만 추가하면 끝
    • B코드에서는 함수참조를 사용할 수 없으므로 함수를 추가로 사용하여 수정해야한다 ( 좀 더 어려움 )
  • 디버깅
    • 일반적으로 디버깅 도구조차 기본적인 구조를 더 잘 분석해 준다
  • 가독성은 '뇌가 프로그램의 작동 방식을 이해하는 과정'을 더 짧게 만드는 것

 

3. 극단적이 되지 않기

  • let으로 인해 예상치 못한 결과를 발생할 수 있다
  • 안전호출 let
class Person(private val name: String) {
    private var person: Person? = null

    fun printName() {
        person?.let { println(it.name) }
    }
}


val person = Person("주지민")
person.printName()
  • 주로 사용하는 시기
    • 지정된 값이 null이 아닌 경우 코드를 실행해야 할때 사용
    • 연산을 아규먼트 처리후 이동시킬 때
    • 데코레이터를 통해 객체를 랩할 때
print(students.filter{}.joinToString{})

=>

students.filter{}.joinToString{}.let(::print)

 

  • 이해하기 어려운 코드지만 충분히 지불할만한 가치를 지닌 코드
students
    .filter { it.result >= 50 }
    .joinToString(separator = "\n") {
    	"${it.name) ${it.surname}, ${it.result}"
    }
    .let(::print)
    
var obj = FileInputStream("/file.gz")
    .let(::BufferedInputStream)
    .let(::ZipInputStream)
    .let(::ObjectInputStream)
    .readObject() as SomeObject

 

 

4. 컨벤션

val abc = "A" { "B" } and "C"
print(abc) // ABC


operator fun String.invoke(f: ()->String): String = this + f()

infix fun String.and(s: String) = this + s
  • 연산자는 의미에 맞게 사용해야한다. invoke는 이렇게 사용하면 안된다
    • invoke: 이름없이 간편하게 호출될 수 있는 함수 ( 람다는 내부에 invoke )
  • '람다를 마지막 아규먼트로 사용한다'라는 컨벤션을 여기에 적용하면, 코드가 복잡해진다. invoke 연산자와 함께 이러한 컨벤션을 적용하는 것은 신중해야합니다
  • 현재 and라는 함수 이름이 실제 함수 내부에서 이루어지는 처리와 맞지 않습니다
  • 문자열 결합하는 함수는 이미 있음, 다시만들필요가 없다
728x90
반응형