전체 보기
🍀

Inheritance vs Composition

작성일자
2024/03/11
태그
DIARY_DEVELOP
프로젝트
WoowaCourse
책 종류
1 more property

Inheritance(상속)

정의) 기존에 정의되어 있는 클래스의 필드와 메서드를 물려받아 새로운 클래스를 생성하는 기법
장점)
중복 코드 제거와 기능 확장을 쉽게 할 수 있음
클래스들의 계층적인 구조를 만들 수 있음
단점)
하위 클래스가 상위 클래스의 구현에 의존하기 때문에 변경에 취약함
public class Cards { private final int BUST_CONDITION; protected List<Card> cards; // List가 아니라 Map으로 수정된다면 public Cards(List<Card> cards) { this.cards = cards; } public boolean isBust() { return calculateScoreTotal() > BUST_CONDITION; }
Java
복사
public class DealerCards extends Cards { public DealerCards(List<Card> cards) { super(cards); } public void addCard(final CardGenerator cardGenerator) { while (carculateScoreTotal() < ADD_CONDITION) { cards.add(cardGenerator.pick()); } }
Java
복사
public class PlayerCards extends Cards { private final String name; public PlayerCards(List<Card> cards, String name) { super(cards); this.name = name; } public void hit(final CardGenerator cardGenerator) { cards.add(cardGenerator.pick()); }
Java
복사
위 코드에서 만일 Cards의 인스턴스 변수 타입이 List가 아닌 다른 타입으로 수정된다면,
DealerCards와 PlayerCards 모두가 영향을 받는다.
자식 클래스들이 부모 클래스와 강하게 의존하여
부모 클래스의 캡슐화를 해치고 결합도가 높아진 상태이기 때문이다.
즉, 부모 클래스의 구현을 변경하려면 많은 자식 클래스들까지 변경을 해줘야 하는 상황이 발생했다.
상위 클래스의 모든 퍼블릭 메서드가 하위 클래스에도 반드시 노출됨
불필요한 부모 클래스의 퍼블릭 메서드가 자식 클래스에도 어쩔 수 없이 노출되고,
공개된 부모 클래스의 퍼블릭 메서드가 자식 클래스의 내부 규칙과 맞지 않을 수도 있다.

Composition(조합)

정의) 전체를 표현하는 클래스가 부분을 표현하는 객체를 포함해서 부분 객체의 코드를 재사용하는 방법
구현) 조합하고 싶은 클래스의 인스턴스를 새로운 클래스의 private 필드로 참조하고, 인스턴스의 메서드를 호출하는 방식으로 구현
public class Cards { private final int BUST_CONDITION; protected List<Card> cards; public Cards(List<Card> cards) { this.cards = cards; } public boolean isBust() { return calculateScoreTotal() > BUST_CONDITION; } public void addUntilEnd(final CardGenerator cardGenerator) { while (carculateScoreTotal() < ADD_CONDITION) { cards.add(cardGenerator.pick()); } public void hit(final CardGenerator cardGenerator) { cards.add(cardGenerator.pick()); }
Java
복사
public class DealerCards { private final Cards cards; public DealerCards(Cards cards) { this.cards = cards; } public void addUntilEnd(final CardGenerator cardGenerator) { cards.addUntilEnd(cardGenerator); }
Java
복사
public class PlayerCards { private final Cards cards; private final String name; public PlayerCards(Cards cards, String name) { this.cards = cards; this.name = name; } public void hit(final CardGenerator cardGenerator) { cards.hit(cardGenerator); }
Java
복사
특징)
상속과 달리 부분 객체의 내부 구현이 공개되지 않음
메서드를 호출하는 방식으로 퍼블릭 인터페이스에 의존해서 부분 객체의 내부 구현이 변경되어도 비교적 안전함
부분 객체의 모든 퍼블릭 메서드를 공개하지 않아도 됨

Inheritance vs Composition

상속의 목적 고려하기: 클라이언트 관점에서 두 객체가 동일한 행동을 할 것이라 기대하는가? O(상속)/X(조합)
서브 타이핑: 다형적인 계층구조 구현 → 부모와 자식 행동이 호환됨
서브 클래싱: 다른 클래스의 코드를 재사용 → 부모와 자식 행동이 호환되지 않음
일반적으로 서브 클래싱만을 목적으로 한 상속은 위에 언급한 문제점 일으킴
상속의 대상 고려하기: 상속을 고려하는 두 객체가 IS-A 관계인가? O(상속)/X(조합)
리스코프 치환 원칙: 올바른 상속 관계에선 하위는 언제나 상위로 교체할 수 있다란 원칙
예를 들어 상위를 사각형, 하위를 정사각형으로 뒀을 때
사각형의 높이를 변경한다는 기능(너비 제외)을 정사각형이 커버할 수 없다면
정사각형은 사각형으로 교체할 수 없는 것임
상속이 적절하게 사용되면 조합보다 강력하고, 개발하기도 편리함