공부/자바

[자바] 상속보다는 조합 사용하기

ghhong 2023. 2. 7. 11:11

출처 : https://tecoble.techcourse.co.kr/post/2020-05-18-inheritance-vs-composition/

 

상속보다는 조합 사용하기

상속의 단점 : 캡슐화를 깨뜨린다.

상위 클래스의 구현이 하위 클래스에 의해 노출되는 상속은 캡슐화를 깨뜨린다.

캡슐화가 깨짐으로써 하위 클래스가 상위 클래스에 강하게 결합 의존하게 되고 이는 변화에 유연히 대처할 없다.

 

로또 번호를 갖는 클래스를 보자

public class Lotto {
   
protected List<Integer> lottoNumbers;

public Lotto(List<Integer> lottoNumbers) {
       
this.lottoNumbers = new ArrayList<>(lottoNumbers);
   
}

public  boolean contains(Integer integer) {
       
return this.lottoNumbers.contains(integer);
   
}
   
...
}


lottoNumbers List<Integer> 갖고 있다. Lotto클래스를 상속하는 WinningLotto클래스가 다음과 같다.

public class WinningLotto extends Lotto {
   
private final BonusBall bonusBall;

public WinningLotto(List<Integer> lottoNumbers, BonusBall bonusBall) {
       
super(lottoNumbers);
       
this.bonusBall = bonusBall;
   
}

public long compare(Lotto lotto) {
       
return lottoNumbers.stream()
           
.filter(lotto::contains)
           
.count();
   
}
   
...
}

 

 

만약 Lotto클래스(상위 클래스의 lottoNumbers 멤버의 자료형이 int[] 변경된다면, 부모와 강한 결합을 맺고 있는 WinningLotto 클래스는 많은 수정이 필요하다.(변화에 대처하기 어렵다)

 

그래서 조합(Composition) 사용한다.

조합은 기존 클래스가 새로운 클래스의 구성요소 사용된다.

새로운 클래스를 만들고 private 필드로 기존 클래스의 인스턴스를 참조한다.

 

앞의 WinningLotto Lotto 상속이 아닌 조합을 하면 다음과 같다.

public class WinningLotto {
   
private Lotto lotto;
   
private BonusBall bonusBall;
}

 

 

WinningLotto클래스는 멤버변수인 Lotto 메서드를 호출하는 방식으로 사용된다.

 

이처럼 조합을 사용하면 멤버변수의 메서드를 호출하는 방식으로 동작하기 때문에 캡슐화를 깨뜨리지 않는다. 또한 Lotto클래스의 구조가 변경돼도 영향이 적다.

다만, 자기 자신에 대한 참조를 다른 객체에 넘겨, 필요할 callback하여 사용하기에는 적합하지 않다.

 

하지만 조합이 상속보다 무조건 좋은 것은 아니다. 상속이 적절히 사용되려면 확장을 고려하여 설계된 확실한 is-a관계일 , 그리고 api 결함이 생겨 하위에 전파되어도 괜찮을 경우에 사용하자.

 

 

상속보다는 조합(Composition)을 사용하자.

tecoble.techcourse.co.kr