오늘의 주제, '상속보다는 컴포지션을 사용하라'

큰일났다 컴포지션이 무슨말인지 모르겠다.

자바에서 객체지향을 위해 상속(implements or exntends)를 사용하는건 잘 알려진 지식이다.

 

재사용성을 높이기 위해 나또한 많이 사용해본 방법인데 컴포지션을 사용하라니, 뒤통수 맞은 느낌이다.

일단 책에서 말하는 것은 캡슐화가 깨질수 있으니 상속이 그렇게 좋은 방법이 아니라고 말하는데

부모 클래스가 변경될때마다 자식에게 영향을 줄 수 있으니 얼추 맞는 말이긴하다.

 

그렇기 때문에 책에서도 순수한 Is-a 관계일 때만 사용한다고 말한다.

그런데 대체 컴포지션은 무엇인가?

 

책에서는 이렇게 말한다.

기존 클래스를 확장하는 대신, 새로운 클래스를 만들고 private 필드로 기존 클래스의 인스턴스를 참조하게 만든다.
기존 클래스가 새로운 클래스의 구성요소를 쓰인다는 뜻에서 이러한 설계를 composition이라 부른다.
새 클래스의 인스턴스 메소드들은 기존 클래스의 대응하는 메소드를 호출 해 그 결과를 반환한다.

이렇게만 말하면 개발자들은 와닿지 않는다, 책의 예제를 활용해 코드로 봐보겠다.

 

HashSet을 상속받은 test클래스 만들어서 addAll을 사용해보았다.

 

 

예상대로라면 addCount는 3이 나와야 한다.

그런데 띠용? 뜬금없이 6이 튀어나온다.

그 이유는

HashSet은 AbstractCollection을 상속받는데 거기서 정의된 addAll을 보면 다음처럼 되어있다.

addAll 메소드 내부에서 add메소드를 사용해 구현해 있다.

addAll은 addCount에 3을 더한뒤 add 메소드가 또 사용되어 3이 더해져 6이 나오게 된것이다.

 

이런 문제를 피하는것이 위에서 말한 컴포지션이다.

컴포지션 구성에서 사용하는 방식이 전달(forwarding)이며

새 클래스의 메소드들을 전달 메소드(forwarding method)라고 부른다.

 

이렇게 할경우에 기존 클래스 내부 구현 방식에서 벗어나서 영향을 받지 않게 된다.

 

 기존에 test<E>클래스는 HashSet<E>를 바로 상속 받아 사용했다.

그러나 Set<E>를 상속받아 재정의한 ForwardingSet<E>을 test<E>가 상속받는다면

위와 같은 문제는 발생하지 않는다.

 

사실 이정도가 되면 그냥 클래스 하나를 새로 파는게 낫지 않나라는 생각이 들기도 하지만

내가 아직 경험이 없어서 그런가보다라고 생각한다.

 

아무튼 상속을 생각할 때 내부적으로 발생할 수 있는 문제에 대해 생각해볼수 있는 챕터였다.

+ Recent posts