불변 객체 설계, 상속은 독? Kotlin 기반 불변성 보장 방법
LY Corporation의 코드 품질 개선 노력의 일환으로 '제약 조건에도 상속세가 발생한다' 주제를 공유
Kotlin의 `IntArray`와 `ImmutableIntList` 예시를 통해 불변 객체의 상속 시 문제점 지적
상속 가능한 불변 클래스는 자식 클래스에서 불변성이 깨질 위험 존재
불변성을 보장하려면 상속 불가능하게 설계하거나, 읽기 전용 인터페이스 활용
불변 객체 상속의 위험성
불변 객체를 상속 가능하게 설계하면 자식 클래스에서 불변성을 훼손할 수 있다. `ImmutableIntList`를 상속받는 `MutableIntList` 예시처럼, 상속을 통해 내부 상태를 변경하는 코드를 쉽게 추가할 수 있다. 따라서 불변성을 보장하려면 상속을 막거나, private 변수 설정만으로는 부족하다.
상속을 대체하는 설계 전략
불변성을 유지하면서 기능을 확장하려면 상속 대신 합성(Composition) 또는 독립적인 타입 정의를 고려해야 한다. `ImmutableSortedIntList`처럼 새로운 기능을 추가할 때, `ImmutableIntList`를 상속하는 대신, `IntArray`를 내부적으로 가지고 기능을 조합하는 방식을 선택할 수 있다. 이는 결합도를 낮추고 유연성을 확보하는 데 기여한다.
가변/불변 객체의 상속 관계
가변 객체와 불변 객체는 서로 상속 관계에 놓이지 않도록 설계해야 한다. 가변 객체가 불변 객체를 상속하면 불변성 제약 조건이 무너지고, 불변 객체가 가변 객체를 상속하면 런타임 에러가 발생할 수 있다. 공통 부모 클래스가 필요한 경우, 읽기 전용(read-only) 인터페이스를 활용하여 안전하게 설계할 수 있다.
Kotlin List의 불변성 설계
Kotlin의 `List`는 읽기 전용 인터페이스를 통해 불변성을 보장하는 좋은 예시이다. 가변 객체는 읽기 전용 객체를 상속받아 안전하게 확장할 수 있으며, 불변 객체는 읽기 전용 객체를 상속받아 불변성을 유지할 수 있다. 이는 상속 관계의 제약 조건을 효과적으로 활용한 설계 방식이다.