본문 바로가기

JAVA

[JAVA] 상속과 조합

728x90

상속과 조합 어떻게 다를까?

상속

상속은 말 그대로 A라는 클래스가 부모가 되어 모든 역할과 구현을 B라는 클래스에게 노출하는 것이다.

 따라서 B는 A와 강하게 결합된다.

 

상속은 반드시 is-A관계가 성립할 때만 사용되어야 한다.

is-A
'B는 A다'가 성립해야 한다.
따라서 A의 캡슐화는 깨지고, 두 객체는 강하게 결합된다.
동물과 사자를 예로 들면, '사자는 동물이다'는 성립한다.
따라서 B는 반드시 A에서 파생되는 클래스여야 한다.

조합

조합은 위 A라는 특정 역할을 수행하는 객체를 B에서 멤버로 가지는 것을 말한다.

이때 B는 필요로 하지만, 본인의 역할을 벗어난 작업을 A에게 요청할 것이다.

 

B는 A에게 요청을 보내고 응답을 받는 유연한 협력 관계를 설계할 수 있다.

A는 B에게 필요한 역할을 수행하는 것이고, 동일한 역할의 구현체와 교체될 수 있다.

has-A
'B가 A를 가진다'가 성립해야 한다.
A는 B의 멤버로 존재하여 캡슐화를 지킬 수 있고, 두 객체는 느슨하게 결합된다. 

왜 조합을 먼저 고려해야 할까?

위에서 말했듯 상속 관계에서 두 객체는 강하게 결합한다.

강하게 결합되면, 유연한 협력 관계를 형성할 수 없게 된다.

 

로또 클래스 객체를 보자.

Lotto.java

import java.util.ArrayList;  
import java.util.List;  
  
public class BasicLotto {  
  
    protected final List<Integer> lotto;  
  
    public BasicLotto(List<Integer> lottoNumbers) {  
        this.lottoNumbers = new ArrayList<>(lottoNumbers);  
    }  
  
    public boolean contains(Integer num) {  
        return lotto.contains(num);  
    }  
    ...
}

로또 객체는 특정 숫자가 해당 로또에 있는지 확인, 매개 변수로 오는 로또와의 비교 결과 계산 등

로또라는 객체와 관련된 로직들을 수행할 것이고, 그게 로또 객체의 역할이 될 것이다.

 

이제 보너스 번호를 포함한 보너스 로또 객체를 보자.

BonusLotto.java

public class BonusLotto extends BasicLotto{  
  
    private final int bonusBall;  
  
    public BonusLotto(List<Integer> lottoNumbers, int bonusBall) {  
        super(lottoNumbers);  
        this.bonusBall = bonusBall;  
    }  
  
    public long compare(Lotto lotto) {  
        return lottoNumbers.stream()  
            .filter(lotto::contains)  
            .count();  
    }  
}

위 코드는 상속을 받았을 때의 코드이다.

BasicLotto라는 클래스를 상속받아 보너스 번호 멤버만을 추가해 구현할 수 있다.

상속을 이용하면, BasicLotto와 중복되는 코드를 없앨 수 있다는 장점이 있다.

 

하지만 만약 Lotto 역할을 다른 방법으로 구현해야 한다고 해보자.

OtherLotto.java

import java.util.ArrayList;  
import java.util.List;  
  
public class OtherLotto {  
  ... 
}

이때 상속으로 구현한 BonusLotto는 어떻게 되는 걸까

BasicLotto와 강하게 결합됐기 때문에 OtherLotto에 대해서는 적용될 수 없는 객체일 것이다.

 

만약 상속받는 대상을 OtherLotto와 바꾼다면, BonusLotto 객체부터 협력 관계를 맺고 있는 객체들까지

수정해야 할 범위가 프로젝트 범위에 비례해 커질 것이다.

 

하지만 만약 조합을 활용해 Lotto라는 역할의 객체와 BonusLotto의 결합을 느슨하게 했다면

Lotto 역할의 객체 내부 구현이 노출되지 않고, 역할만을 고려하기 때문에 구현체만 OtherLotto로 교체함으로써 변경 사항에 유연하게 대응할 수 있을 것이다.

 

[OOP] 디미터의 법칙

개요 최근 계속해서 객체 지향에 대한 공부를 하던 중 디미터의 법칙을 접하게 되었다. 객체 지향의 핵심을 꿰고 있는 개념이라고 생각이 되어 이에 대한 내 생각을 기록으로 남기려고 한다. 디

choi-records.tistory.com

상속은 반드시 is-A 관계에서만 사용하고, 조합을 우선적으로 고려하자!

마무리

제 부족한 경험과 지식으로 작성한 글이니 잘못된 부분이 있을 수 있습니다.

이에 대한 지적이나 질문은 환영입니다. 감사합니다🙇‍♂️

728x90