반응형

아이템 36: 상속보다는 컴포지션을 사용하라

일반적으로 공통되는 행위를 여러개의 클래스에서 구현할 때 상속을 사용한다.

하지만, 상속을 사용함에 있어 관계가 명확하지 않다면 여러가지 문제 발생할 수 있다.

상속의 문제점

  • 코틀린에서의 상속은 하나의 클래스만 가능 -> Base 클래스가 거대해지고 복잡해질 수 있음
  • 클래스의 모든 것을 가져오게 된다. 불필요한 함수를 override해야 할 수도 있음(인터페이스 분리 원칙 위배)
  • 상속은 이해하기 어려움. 개발자가 메서드를 이해하기 위해 슈퍼클래스를 여러번 확인한다면 문제가 있는 코드임

 

이러한 이유로 다른 대안인 “컴포지션(composition)”을 활용한다.

컴포지션은 객체를 프로퍼티로 갖고, 함수를 호출하는 형태로 재사용하는 것이다.

컴포지션은 아래 코드처럼 활용한다.

추가 코드를 간결하게 처리하는것이 어려워 상속을 선호하는 사람들도 있지만, 더 명확하고 자유롭게 사용할 수 있다는 장점이 있다.

class Progress{
	fun showProgress(){ /* show progress */ }
    fun hideProgress(){ /* hide progress */ }
}

class ProfileLoader{
	val progress = Progress()
    
    fun load(){
    	progress.showProgress()
        // 프로필을 읽어 들임
        progress.hideProgress()
    }
}

class ImageLoader{
	val progress = Progress()
    
    fun load(){
    	progress.showProgress()
        // 이미지를 읽어 들임
        progress.hideProgress()
    }
}

컴포지션을 활용하면 아래처럼 하나의 클래스 내부에서 여러 기능을 재사용할 수 있게 된다.

class ImageLoader{
	private val progress = Progress()
    private val finishedAlert = FinishedAlert()
    
    fun load(){
    	progress.showProgress()
        // 이미지를 읽어 들임
        progress.hideProgress()
        finishedAlert.show()
    }
}

 

정리

컴포지션과 상속의 차이점

  • 컴포지션은 더 안전하다. 다른 클래스의 내부적인 구현에 의존하지 않고, 외부에서 관찰되는 동작에만 의존함.
  • 컴포지션은 더 유연하다. 여러 클래스를 대상으로 할 수 있다.  상속과 달리 필요한 기능만 사용할 수 있다.
  • 컴포지션은 더 명시적이다. 무조건적으로 명시적으로 활용해야 하므로 확실하게 알 수 있지만, 번거롭기도 하다.

컴포지션 vs 상속

이펙티브 코틀린에서는 일반적으로 OOP에서는 상속보다 컴포지션을 사용하는것이 좋다고 한다. 

그렇다면 상속은 언제 사용하면 좋을까?

  • 슈퍼클래스와 서브클래스가 명확한 "is-a" 관계일 때 활용하는게 좋다.
  • 슈퍼클래스를 상속하는 모든 서브클래스가 슈퍼클래스로도 동작할 수 있어야 한다.
  • 슈퍼클래스의 모든 단위 테스트는 서브클래스로도 통과할 수 있어야 한다.

대표적으로는 Android의 Activity.

또한, 상속을 위해 설계되지 않은 메서드는 final로 만들어 두는 것이 좋다.

아이템 37: 데이터 집합 표현에 data 한정자를 사용하라

 

아이템 38: 연산 또는 액션을 전달할 때는 인터페이스 대신 함수 타입을 사용하라

 

아이템 39: 태그 클래스보다는 클래스 계층을 사용하라

 

아이템 40: equals의 규약을 지켜라

 

아이템 41: hashCode의 규약을 지켜라

 

아이템 42: compareTo의 규약을 지켜라

 

아이템 43: API의 필수적이지 않은 부분을 확장 함수로 추출하라

 

아이템 44: 멤버 확장 함수의 사용을 피하라

반응형

+ Recent posts