wanna be dev 🧑‍💻

Cool 하고 Sick한 개발자가 되고 싶은 uzun입니다

A.K.A. Kick-snare, hyjhyj0901, h_uz99 solvedac-logo

Kotlin

[이펙티드 코틀린] 3장 ♻️ 재사용성 Reusability (item 19..25)

Kick_snare 2023. 6. 28. 13:52
728x90

by Vincent Dörig

item19 : knowledge를 반복하여 사용하지 말라

프로젝트에서 이미 있던 코드를 복사해서 붙여넣고 있다면, 무언가가 잘못된 것이다

knowledge가 뭘까

  • 넓은 의미로 의도적인 정보
  • 비즈니스 로직 또는 공통 알고리즘
    • 로직은 시간에 따라 변활 수 있다

왜 반복해서 쓰면 안되는데

  • 프로그래밍에서 유일하게 유지되는 것은 "변화한다는 속성"
  • knowledge가 반복되면 변화에 취약하다.
  • 이는 프로젝트의 scalable을 제한하고, fragile하게 한다.
  • 반복되는 부분을 추출하여 재사용성을 높임으로써 해결

이건 반복이 아니에요 추출멈춰

  • 반복처럼 보이는데 사실 알고보니 짜잔 다른 knowledge였습니다
    • 2개의 안드로이드 프로젝트에서 빌드 설정이 비슷하다고 추출하면 안됨
  • 어떻게 판단하지 그럼
    • 비즈니스 규칙이 다른 곳(소스)에서 왔는지 확인
    • SRP 단일 책임 원칙을 따른다

SRP 단일 책임 원칙

  • actor가 같은 클래스를 변경하는 일은 없어야한다.
    • actor : 변화를 만들어 내는 존재 (source of change)
  • SRP로 부터 알 수 있는 것.
    • 서로 다른 곳에서 사용하는 knowledge는 독립적으로 변경할 가능성이 높다
    • 다른 knowledge는 분리하는 것이 좋다 (개발자를 유혹함)

item20 : 일반적인 알고리즘을 반복해서 구현하라

이미 구현된 알고리즘을 활용하면

  • 코드 작성이 빨라짐
  • 함수의 이름만 보고 동작 유추 가능
  • 실수를 줄일 수 있다
  • 간단 알고리즘은 stdlib 대부분 구현되어 있다

커스텀 유틸 만들기

  • 동일한 결과를 얻는 함수를 여러번 만드는건 멋지지 않아
  • 따라서 자신이 만들라는게 있는지 찾아보도록 하자
  • 유틸을 만들어 추출해내는 방법에는 여러가지(top-level 함수, 프로퍼티 위임, 클래스 등)가 있는데 그 중 여러모로 확장함수가 좋다. 왜?
    • 함수는 상태가 없으므로 행위를 나타내기 좋다
    • top-level 함수에 비해 타입을 제한할 수 있다.
    • arg로 받아 쓰는 것 보다 가독성에 좋다
    • 자동완성 기능 활용 가능해서 찾기 쉽다

item21 : 일반적인 프로퍼티 패턴은 프로퍼티 위임으로 만들어라

프로퍼티 위임이란

  • 다른 객체의 메서드를 활용해서 프로퍼티의 접근자를 만드는 방식

프로퍼티 위임은

  • 프로퍼티의 동작을 추출해서 재사용할 수 있다.
  • 프로퍼티 패턴을 활용하면 다양한 패턴을 만들 수 있다.
    • EX ) lazy 프로퍼티 / observable
  • 코틀린에서 간단하고 type-safe하게 구현할 수 있다.

코틀린으로 구현하기

private class LoggingProperty<T>(
    varvalue:T
) { 
    operator fun getValue(
        thisRef: Any?,
        prop: KProperty<*>
   ): T {
       print("${prop.name} returned value $value")
       return value 
   }

    operator fun setValue(
        thisRef: Any?,
        prop: KProperty<*>,
        newValue: T 
    ) {
        val name = prop.name  
        print("$name changed from $value to $newValue") 
        value = newValue
    } 
}
  • getValuesetValue 를 정의한다.
  • getValuesetValue가 여러개 있어도 context에 따라 다르게 사용 가능

item22 : 일반적인 알고리즘을 구현할 때 제네릭을 사용하라

제네릭 함수는

  • type arg를 가지는 함수를 제네릭 함수라고 한다.
  • 컴파일러에 타입 관련 정보를 제공해 타입을 더 정확하게 추측할 수 있게 함
  • 컴파일 과정에서 타입 정보는 사라지지만 개발중에 특정 타입을 강제할 수 있다.

제네릭 제한하기

  • supertype 지정하기
    • fun <T: Comparable<T>> Iterable<T>.sorted() : List<T>
  • Any로 Non-nullable
    • fun <T, R: Any> Iterable<T>.mapNotNull() : List<R>

item23 : 타입 파라미터의 섀도잉을 피하라

섀도잉 shadowing이란

  • 프로퍼티와 파라미터가 같은 이름을 가진다면?
  • local 파라미터가 클래스의 프로퍼티를 가림
  • 이를 섀도잉이라고 한다.
  • 타입 파라미터에서도 일어나는 현상
interface Tree
class Birch: Tree
class Spruce: Tree    

class Forest<T: Tree> {  
    fun <T: Tree> addTree(tree: T) { ... }  
}

그러니까 이름 다르게 쓰세요~~ 라고 하심

item24 : 제네릭 타입과 variance 한정자를 활용하라

Invariant 불공변성, Covariant 공변성, Contravariant 반병성

  • Invariance 불공변성
    • AB의 서브타입일 때 , Box<A>Box<B>는 아무런 관련성이 없음
  • Covariant 공변성
    • AB의 서브타입일 때 , Box<A>Box<B>의 서브타입이다.
  • Contravariant 반변성
    • AB의 서브타입일 때 , Box<A>Box<B>의 슈퍼타입이다.

variance 한정자 in & out

class Cup<T>
class Cup<in T>
class Cup<out T>
  • 제네릭 타입에서
    • 기본적으로 invariance 불공변성
    • in : covariant 공변성을 나타냄
    • out : contravariant 반변성을 나타냄
  • 함수 타입에서
    • 코틀린의 모든 함수 파라미터 타입은 contravariant 반변성 (out)
    • 코틀린의 모든 함수 리턴 타입은 covariant 공변성 (in)

variance 한정자의 안정성

  • 자바의 배열은 covariant -> 명백한 결함이 존재한다.
  • Interger[] numbers = {1, 2, 3, 4} Object[] objects = numbers objects[2] = "B"
  • 따라서 코틀린에서는 invariant한 Array를 사용
    • IntArray, CharArray, BooleanArray
  • Immutable Collection (List, Set)은 convariant(out)
    • 변경 불가능 하기에 convariant해도 문제 없음
  • 반대로 mutable Collection은 위 Array 문제와 같이 invariant

variance 한정자의 위치

  1. 클래스와 인터페이스 선언부
    • class Box<out T>(val value: T)
  2. 클래스와 인터페이스를 시용하는 위치
    • val boxAny: Box<out Any> = Box<String>("Str")
728x90