상속, 인터페이스, 트레이트 차이점과 어떨 때 사용하나.
상속, 인터페이스, 트레이트의 차이
상속과 나머지 두 관계의 가장 큰 차이점은, 인터페이스와 트레이트는 기능 단위 라는 것이다. 그래서 클래스 사이에 연관 관계가 없어도 기능 단위의 공통점을 가지도록 할 수 있다.
abstract
추상 클래스를 사용하려면 extends
를 사용해야 하므로, 상속 계층에 연결된다. 추상 클래스는 반드시 하나 이상의 추상 메소드를 포함해야 한다. 추상 메소드는 하위 클래스에서 반드시 재정의되어야 한다. interface와 달리 일반 메소드도 포함할 수 있다. 상속 계층을 먹고 들어간다는 점, 메소드 재정의 강제는 interface로도 가능하다는 점 때문에 신중하게 써야한다.
interface
인터페이스에 어떤 메서드가 있는지 알고 있다면, 각 클래스에서 인터페이스를 어떻게 구현했는지는 신경 쓸 필요 없이 그냥 호출하면 된다. 파라미터 타입으로 인터페이스를 적으면, 기능 단위 공통점을 제외하면 서로 연관 관계가 없는 클래스들을 모두 받을 수 있다.
trait
상속과 달리 여러 개를 Mixin
할 수 있으며 인터페이스와 달리 구현부를 가질 수 있다. 그래서 상속과 인터페이스의 중간 쯤으로 생각할 수 있으나, 기능 단위라는 점에서 인터페이스에 더 가까운 듯.
이러한 성격 때문에,
인터페이스는 각 클래스 마다 각자의 사정에 맞게 구현해 내부 처리가 모두 다를 때 사용하고
트레이트는 모든 클래스에서 구현이 동일해야 할 때 사용한다.
단일 상속 모델은 기본적인 구현을 제공하는 일반화된 하나의 루트 클래스에서 시작해서 이 루트 클래스를 확장해 나가면서 바로 윗 부모의 구현을 상속받아 좀 더 특화된 클래스를 생성하게 된다.
이를 상속 계층이라 부른다. 이러한 상속 계층은 대부분의 경우 잘 동작하지만 연관이 없는 두 클래스가 유사한 속성을 갖도록 해야 하는 경우 문제가 발생한다. 예를 들어 Car
와 RetailStore
는 상속 계층상 공통의 부모를 공유하지 않지만, 두 클래스 모두 지도 위에 표시될 위치정보를 나타낼 수 있어야 한다. 트레이트는 이러한 목적을 위해 사용된다. 즉, 트레이트는 모듈화된 구현을 서로 관련 없는 클래스들에 주입하기 위해 사용한다. 새로운 부모 클래스를 상속? → 기존 상속 계층을 깨트리기 때문에 당연히 부자연스럽다. 인터페이스를 구현? → Car
와 RetailStore
에 같은 구현이 중복된다. 이는 DRY 하지 않다. 트레이트를 사용하면 상속 계층을 깨트리지 않으면서 구현은 중복하지 않을 수 있다. * 이럴거면 다중 상속을 지원하면 되는거 아닌가? 싶지만 클래스 상속과 트레이트 사용은 상기했듯이 그 의미가 다르다. 상속은 상속 계층을 따라 좀 더 부모와 직접적인 관련이 있는 느낌, 부모를 좀 더 구체화해서 구현하는 느낌이다. 어떤 클래스를 상속한다는 것은 그 클래스를 근간으로 한다는 것을 의미한다. 반면 트레이트는 모듈화된 구현을 가져다 쓰는 import
의 느낌, 단순한 기능 추가의 느낌이 강하다.