Hyeyeon blog

이펙티브 코틀린 - 3장 재사용성, 4장 추상화 설계 본문

개발/Android

이펙티브 코틀린 - 3장 재사용성, 4장 추상화 설계

Hyeyeon.P 2024. 4. 8. 18:30
반응형

3장 재사용성

19. knowledge를 반복하여 사용하지 않기 
20. 일반적인 알고리즘을 반복해서 구현하지 않기 
21. 일반적인 프로퍼티 패턴은 프로터피 위임으로 생성하기
22. 일반적인 알고리즘은 제네릭 함수로 구현하기 
23. 타입 파라미터의 섀도잉을 피하기
24. 제네릭 타입과 variance 한정자(out, in) 활용하기
25. 공통 모듈로 추출해서 여러 플랫폼에서 재사용하기

4장 추상화 설계

26. 함수 내부의 추상화 레벨을 통일하라
27. 변화로부터 코드를 보호하려면 추상화를 사용하라
28. API 안정성을 확인하라
29. 외부 API를 랩(wrap)해서 사용하라 
30. 요소의 가시성을 최소화하라 
31. 문서로 규약을 정의하라
32. 추상화 규약을 지켜라 


19. knowledge를 반복하여 사용하지 않기 

* knowlege: 의도적인 정보 = 코드 = 데이터

* 단일 책임 원칙: 하나의 클래스는 하나의 역할을 갖는다. 

20. 일반적인 알고리즘을 반복해서 구현하지 않기 

표준 라이브러리에 있는 알고리즘은 별도로 구현하지 않는다.
라이브러리에 없는 알고리즘은 유틸리티 함수로 구현한다. 

21. 일반적인 프로퍼티 패턴은 프로터피 위임으로 생성하기

프로퍼티 위임을 사용하여 프로퍼티의 행위를 추출해서 재사용할 수 있다.

대표적인 프로퍼티 델리게이터 
- lazy
- Delegates.observable
- Delegates.vetoable
- Delegates.notNull

22. 일반적인 알고리즘은 제네릭 함수로 구현하기 

23. 타입 파라미터의 섀도잉을 피하기

* 섀도잉: 지역 파라미터가 외부 스코프에 있는 프로퍼티를 가리킴 (프로퍼티와 파라미터가 같은 이름을 같는 경우) 

섀도잉 예시

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

24. 제네릭 타입과 variance 한정자(out, in) 활용하기

* invariance: 제네릭 타입으로 만들어지는 타입들이 서로 관련성이 없음
* out: 타입 파라미터를 convariant(공변성)으로 만듦
* List, Set: convariant (out 한정자) 
* MutableList, MutableSet, MutableMap: invariant (한정자 지정 없음)

1. out 예시
class Cup<out T>
open class Dog
class Puppy: Dog()

val b: Cup<Dog> = Cup<Puppy> // Ok
val a: Cup<Puppy> = Cup<Dog> // 오류 

2. in 예시 
class Cup<in T>
open class Dog
class Puppy: Dog()

val b: Cup<Dog> = Cup<Puppy> // 오류
val a: Cup<Puppy> = Cup<Dog> // Ok

25. 공통 모듈로 추출해서 여러 플랫폼에서 재사용하기

공통되는 비즈니스 로직을 공통 모듈로 추출하여 재사용한다. 
모바일 개발 시, 코틀린의 멀티 플랫폼 기능을 활용하여 로직을 한번만 구현하고 두 플랫폼에서 이를 재사용할 수 있다. 

26. 함수 내부의 추상화 레벨을 통일하라

추상화는 복잡성을 숨기기 위해/ 코드를 체계화하기 위해/ 만드는 사람에게 변화의 자유를 주기위해 사용된다.
* 추상화 레벨 통일 (Single Level of Abstraction, SLA) 원칙: 높은 레벨, 낮은 레벨을 구분하여 함수를 사용해야한다. 

* Before
class CoffeeMachine {
    fun makeCoffee() {
        // 여러 변수 선언, 복잡한 로직 처리 
    }
}

* After
class CoffeeMachine {
    fun makeCoffee() {
        boilWater()
        brewCoffee()
        pourCoffee()
        pourMilk()
    }
}

27. 변화로부터 코드를 보호하려면 추상화를 사용하라

- 상수로 추출한다 --> 이름을 붙일 수 있고, 나중에 해당 값을 쉽게 변경할 수 있다. 
- 동작을 함수로 래핑한다. --> 이름을 갖지며 이름으로 어떤 추상화를 표현하는 지 알 수 있다. 

* Before 
fun Context.toast() {
    // ...
}

fun Context.snackbar() {
    // ...
}

* After
중요한건 메시지의 출력 방법이 아닌, 메시지를 출력하겠다는 의도 자체임. 
따라서 메시지를 출력하는 더 추상적인 방법이 필요함.
토스트 출력을 토스트라는 개념과 무관한 showMessage라는 높은 레벨의 함수를 사용해야함.

fun Context.showMessage() {
    // ...
}


- 함수를 클래스로 래핑한다 --> 상태를 가질 수 있으며, 많은 함수를 가질 수 있다. 
- 인터페이스 뒤에 클래스를 숨긴다 --> 인터페이스 뒤에 객체를 숨김으로써 실질적인 구현을 추상화한다. 결합을 줄일 수 있음. 
- 보편적인 객체를 특수한 객체로 래핑한다. 

28. API 안정성을 확인하라

29. 외부 API를 랩(wrap)해서 사용하라 

잠재적으로 불안정하다고 판단되는 외부 라이브러리 API는 랩해서 사용하여 안정성을 확보한다.
* 장점: 문제가 있다면 래퍼만 변경하면되므로 API 변경에 쉽게 대응 가능 
* 단점: 래퍼를 따로 정의해야함. 

30. 요소의 가시성을 최소화하라 

작은 인터페이스와 기능이 적은 클래스는 이해와 유지보수가 쉽다.
즉, 보이는 요소 자체가 적을수록 유지보수 및 테스트할 것이 적고 변경을 쉽게 추적할 수 있으며, 프로퍼티 상태를 쉽게 이해할 수 있다. 
가시성 한정자를 활용하여 외부 노출이 불필요한 클래스와 요소의 가시성을 제한한다.
* 가시성 한정자: public, private(클래스 내부 노출), protected(클래스, 서브클래스 내부 노출), internal (모듈 내부 노출) 
* 모듈: gradle, maven과 같이 함께 컴파일되는 코틀린 소스 

31. 문서로 규약을 정의하라

규약은 이름, 문서, 주석, 타입을 통해 구현하며, 요소가 현재 어떻게 동작하고 앞으로 어떻게 동작할지를 사용자에게 전달한다.
이를 기반으로 사용자는 요소를 확실하게 이해할 수 있고, 규약에 없는 부분을 변경할 수 있는 자유를 얻는다. 

32. 추상화 규약을 지켜라 

안정적으로 프로그램을 유지하기 위해서는 규약을 지켜야한다. 
규약을 깰 수 밖에 없다면, 이를 잘 문서화해야한다. 

728x90
Comments