-
[이펙티브 코틀린]가변성을 제한하라전공 도서 리뷰 2022. 6. 4. 00:36
요소가 시간의 변화에 따라 변화하는 경우의 단점
- 프로그램을 이해하고 디버깅하기 힘들어짐
- 코드의 실행을 추론하기 어려워짐.
- 멀티스레드 프로그래밍의 경우 동기화가 필요하다
- 변경점이 많을 수록 충돌점이 많아진다.
- 테스트하기 어렵다
- 변경이 많을 수록 많은 조합을 테스트해야 한다.
- 상태 변경을 다른 부분에 알려야 할 때도 있다.
가변성 제한 방법
- 읽기 전용 프로퍼티(val)
- 완전히 변경 불가능하지 않다.
- 다른 프로퍼티를 활용하는 사용자 정의 게터로도 정의 가능하다.
- 정의 옆에 상태가 바로 적히므로 코드의 실행을 예측하는 것이 간단
- 스마트 캐스트를 활용 가능
- 가변 컬렉션과 읽기 전용 컬렉션 구분하기
- 읽기 전용 컬렉션을 가변 컬렉션으로 다운캐스팅하면 안 된다.
- 읽기 전용 컬렉션 사용 시 장점
- 한 번 정의된 상태가 유지되므로 코드를 이해하기 쉽다
- 공유했을 때도 충돌이 이뤄지지 않으므로 병렬 처리가 안전하다.
- 객체에 대한 참조가 변경되지 않으므로 쉽게 캐시할 수 있다.
- 방어적 복사본을 만들 필요가 없다.
- 객체를 복사할 때 깊은 복사를 따로 하지 않아도 된다.
- 다른 객체를 만들 때 활용하기 좋다.
- 실행을 더 쉽게 예측 가능하다.
- set 또는 map의 키로 사용 가능.
- 키 값을 기반으로한 자료구조는 요소의 변경이 일어나면 찾을 수 없다.
- 데이터 클래스의 copy
- copy를 사용하여 immutable 객체를 수정할 수 있다.
변경 가능 지점
- mutable 컬렉션
- ex)val list1 = MutableList<Int>
- 리스트 구현 내부에 변경 가능 지점이 있다.
- 멀티스레드 처리가 이뤄질 경우 동기화되어 있는지 확실히 알 수 없어 위험하다.
- mutable 프로퍼티
- ex)var list2 = List<Int>
- 프로퍼티 자체가 변경 가능 지점.
- 멀티스레드 처리의 안정성이 더 좋음.
- 사용자 정의 세터를 활용하여 변경을 추적 가능.
- Delegates.observable을 사용 시 변경 시에 로그를 출력할 수 있다.
- 객체 변경을 제어하기가 더 쉽다.
- 프로퍼티와 컬렉션 둘 다 mutable인 경우
- 두 지점 모두에 대한 동기화를 구현해야 함.
- 모호성이 발생하여 +=를 사용할 수 없다.
변경 가능 지점 노출하지 말기
- 상태를 나타내는 mutable 객체를 외부에 노출하는 것은 굉장히 위험하다.
- 돌발적인 수정이 일어날 때 위험할 수 있다.
- 이를 방지하기 위한 방법
- 방어적 복제
- 리턴되는 mutable 객체를 복제하는 것.
- 읽기 전용 슈퍼클래스로 업캐스트하여 가변성을 제한
- 방어적 복제
- 이를 방지하기 위한 방법
- 돌발적인 수정이 일어날 때 위험할 수 있다.
실제 코드에 적용해본다.
기존 ViewModel에서 사용되던 MutableStateFlow를 예로 들어보자.
class HomeViewModel:ViewModel(){ ... val nickname: MutableStateFlow<String> = MutableStateFlow("") val emoji: MutableStateFlow<String> = MutableStateFlow("") val weatherEmoji: MutableStateFlow<String> = MutableStateFlow("") val weatherDegree: MutableStateFlow<String> = MutableStateFlow("")
각 프로퍼티들이 val로 정의되어 있으나, 사실상 안의 값은 수정이 가능하다.
가변성을 가진 프로퍼티가 외부에 노출되어 있으므로, 캡슐화가 필요하다.
... private val _nickname = MutableStateFlow("") private val _emoji = MutableStateFlow("") private val _weatherEmoji = MutableStateFlow("") private val _weatherDegree = MutableStateFlow("") val nickname: StateFlow<String> get() = _nickname val emoji: StateFlow<String> get() = _emoji val weatherEmoji: StateFlow<String> get() = _weatherEmoji val weatherDegree: StateFlow<String> get() = _weatherDegree
기존 MutableStateFlow 변수는 private 처리하여 외부에서는 접근을 차단하고,
ViewModel 내부에서는 자유롭게 수정가능하도록 한다.
그리고 외부에 노출할 용도로 MutableStateFlow를 업캐스팅하여 수정 불가능하도록 StateFlow 변수를 getter로 정의한다.
이로써 값은 외부에 노출할 수 있으나, 내부의 변수를 수정하지 못하도록 캡슐화를 진행하였다.
'전공 도서 리뷰' 카테고리의 다른 글
[전공 도서 리뷰] 클린 코드 Clean Code 정독을 시작하다. (0) 2020.11.07