ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [🏝 이펙티브 코틀린] 3 재사용성
    Book/Effective Kotlin 2023. 6. 12. 00:50
    반응형

    3장 재사용성

    ⚾️ item 19 knowledge를 반복해서 사용하지 말라

    knowledege = 의도적인 정보 = 로직, 공통 알고리즘

    • 공통 knowledge가 있다면 이를 추출해서 변화에 대비해야 한다.
    • 비즈니스 규칙이 다른 곳에서 왔다면, 독립적으로 변경될 가능성이 높기 때문에 추출을 하면 안된다.
    • 단일 책임 원칙 : 클래스를 변경하는 이유는 단 한가지여야 한다.

    ⚾️ item 20 일반적인 알고리즘을 반복해서 구현하지 말라

    • 대부분 stdlib 에 정의되어 있을 가능성이 높다.
    • stdlib 에 없는 일반적인 알고리즘 또는 특정 알고리즘을 반복해서 사용해야하는 경우는 프로젝트 내부에 직접 정의해라(확장 함수로 정의하는 것이 좋다)

    ⚾️ item 21 일반적인 프로퍼티 패턴은 프로퍼티 위임으로 만들어라

    코틀린은 프로퍼티 위임 이라는 기능을 제공한다. 프로퍼티 위임은 프로퍼티 패턴을 추출하는 일반적인 방법!

    더보기

    💡 프로퍼티 패턴이란?

    프로퍼티 패턴(Property pattern)은 소프트웨어 개발에서 객체 지향 프로그래밍에서 사용되는 디자인 패턴 중 하나입니다.

    이 패턴은 객체의 상태(state)를 캡슐화하고, 상태에 접근하거나 변경하기 위해 메서드를 제공하는 방법을 정의합니다. 즉, 프로퍼티 패턴은 클래스나 구조체의 인스턴스 변수를 보호하고, 외부에서 직접 접근하는 것을 방지하기 위해 사용됩니다.

     

    프로퍼티 패턴의 구성 요소

    1. 프로퍼티(Property): 객체의 상태를 저장하는 인스턴스 변수로, 일반적으로 private으로 선언됩니다. 외부에서는 직접 접근할 수 없고, 접근자(getter)와 설정자(setter) 메서드를 통해 상태에 접근하거나 변경할 수 있습니다.
    2. 접근자(Getter): 프로퍼티의 값을 반환하는 메서드로, 일반적으로 프로퍼티 이름 앞에 "get" 접두사를 붙여서 작성됩니다. 외부에서 프로퍼티 값을 읽을 때 호출됩니다.
    3. 설정자(Setter): 프로퍼티의 값을 설정하는 메서드로, 일반적으로 프로퍼티 이름 앞에 "set" 접두사를 붙여서 작성됩니다. 외부에서 프로퍼티 값을 변경할 때 호출됩니다.

    프로퍼티 위임의 exmaple

    • 지연 프로퍼티 (lazy)
      • 처음 사용하는 요청이 들어올 때 초기화 되는 프로퍼티
      val value by lazy { createValue() }
      
    • Delegate.observable
      • 목록을 출력하는 리스트 어댑터가 내부 데이터가 변경될 때마다 변경된 내용을 다시 출력할 때
      var items: List<Item> by
      	Delegates.observable(listOf()) { _, _, _ ->
      		notifyDataSetChanged()
      }
      
    • Delegates.vetoable
    • Delegates.notNull

    ⚾️  item 22 일반적인 알고리즘을 구현할 때 제네릭을 사용하라

    제네릭 함수란?

    2023.06.08 - [kotlin] - [Kotlin] Generic 제네릭이란?, variance : in, out

     

    [Kotlin] Generic 제네릭이란?, variance : in, out

    제네릭이란? 이렇게 생긴 코드를 많이 봤을 것이다. 이게 바로 제네릭이다. 제네릭을 사용하면 타입 파라미터 (type parameter)를 받는 타입을 정의할 수 있다. 예를 들어, List라는 타입이 있다면 그

    tral-lalala.tistory.com

    타입 아규먼트를 사용하는 함수(즉, 타입 파라미터를 갖는 함수)

    함수 - fun 키워드와 함수 이름 사이에 <T> 형태의 코드를 입력한다.

    클래스와 인터페이스 - 클래스와 인터페이스 이름 뒤에 <T> 형태의 코드를 입력한다.

     

    ex) filter 함수

    inline fun <T> Iterable<T>.filter(
        predicate: (T) -> Boolean
    ): List<T> {
        val destination = ArrayList<T>()
        for (element in this) {
            if (predicate(element)) {
                destination.add(element)
            }
        }
        return destination
    }
    
    • 타입 파라미터는 컴파일러에 타입과 관련된 정보를 제공하여 컴파일러가 타입을 조금이라도 더 정확하게 추측할 수 있게 해준다.
    • 코틀린 자료형 시스템에서 타입 파라미터는 굉장히 중요한 부분이다. 타입 파라미터는 구체 자료형의 서브타입을 제한할 수 있는데, 이는 특정 자료형이 제공하는 메서드를 안전하게 사용할 수 있다.

    ⚾️ item 23 타입 파라미터의 섀도잉을 피하라

    섀도잉이란?

    - 프로퍼티와 파라미터가 같은 이름을 가지는 것

    class Forest(val name: String) {
        fun addTree(name: String) {
            // ...
        }
    }
    

     이 같은 경우에는 지역 파라미터가 외부 스코프에 있는 프로퍼티를 가린다.

    - 클래스 타입 파라미터와 함수 타입 파라미터가 같은 이름을 가지는 것

    interface Tree
    class Birch: Tree
    class Spruce: Tree
    
    class Forest<T: Tree> {
        fun <T: Tree> addTree(tree: T) {
            // ..
        }
    }
    
    

    이 같은 경우에는 Forest와 addTree의 타입 파라미터가 독립적으로 동작

    따라서 add Tree가 클래스 타입 파라미터인 T를 사용하는 것이 좋다.

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

     

    ⚾️ item 24 제네릭 타입과 varinace 한정자를 활용하라 

    다음과 같은 제네릭 클래스가 있다고 해보자

    class Cup<T>
    

    타입 파라미터 T는 variance 한정자(out 또는 In) 이 없으므로 기본적으로 invariant 이다.

    invariant 라는 것은 제네릭 타입으로 만들어지는 타입들이 서로 관련성이 없다는 것이다.

    예를 들어 Cup<Int> 와 Cup<Number>, Cup<Any> 는 서로 어떠한 관련도 없다.

    하지만 어떤 관련성을 원한다면, out 또는 in 이라는 variance 한정자를 붙여야한다.

     

    Out : 타입 파라미터를 Convariant(공변성) 으로 만든다.

    class Cup<out T>
    open class Dogclass Puppy: Dog()
    
    fun main(args: Array<String>){
    	val b: Cup<Dog> = Cup<Puppy>()// OKval a: Cup<Puppy> = Cup<Dog>()// 오류
    }
    

    A가 B의 subtype일 때, Cup<A> 가 Cup<B>의 서브타입이라는 의미이다.

     

    In : 타입 파라미터를 contravariant(반변성) 으로 만든다.

    class Cup<in T>
    open class Dogclass Puppy: Dog()
    
    fun main(args: Array<String>){
        val b: Cup<Dog> = Cup<Puppy>()// 오류val a: Cup<Puppy> = Cup<Dog>()// OK
    }
    

    A가 B의 subtype일 때, Cup<A>가 Cup<B>의 슈퍼타입이라는 의미이다.

     

    ** 정리 **

    • 타입 파라미터 T는 기본적으로 invariant 
    • out 한정자는 타입 파라미터를 convariant 하게, in 한정자는 타입 파라미터를 contravariant 하게 만든다.
    • List, Set, Map 타입 파라미터는 convariant (out 한정자)
    • Array, MuableList, MutableSet, MutableMap의 타입 파라미터는 invariant(한정자 지정 없음)
    • 함수 타입의 파라미터 타입은 contravariant(in 한정자), 리턴 타입은 convariant(out 한정자)

    ⚾️ item 25 공통 모듈을 추출해서 여러 플랫폼에서 재사용하라

    코틀린을 사용하여 개발할 수 있는 플랫폼들

    • kotlin/jvm 백엔드 - Spring, Ktor
    • kotlin/js 웹 사이트 - react
    • koltin/jvm 안드로이드 - android sdk
    • kotlin/native ios
    • 등등

    공통 모듈을 사용하면 여러 플랫폼에서 코드를 재사용할 수 있다.

    반응형
Designed by Tistory.