[Effective Java/아이템18] 상속보다는 컴포지션을 사용하라.
2021. 11. 18. 22:28
반응형
# 상속보다는 컴포지션을 사용하라.
- 상속이 안전할 때
* 상위 클래스와 하위 클래스를 모두 같은 프로그래머가 통제하는 패키지 안에서 사용한다.
* 확장할 목적으로 설계되었고 문서화도 잘 되어있다.
- 상속이 안전하지 않을 때
* 일반적인 구체 클래스를 패키지 경계를 넘어 다른 패키지의 구체 클래스를 상속하는 일은 위험하다. 이때 '상속'은 다른 패키지의 구체 클래스를 확장하는 '구현 상속'을 의미하며, 인터페이스의 상속과는 무관하다.
# 하위 클래스가 깨지기 쉬운 이유
## 1. 메서드 호출과는 달리 상속은 캡슐화를 깨뜨린다.
* 상위 클래스가 어떻게 구현되느냐에 따라 하위 클래스의 동작에 이상이 생길 수 있다.
* 상위 클래스는 릴리즈마다 내부 구현이 달라질 수 있으며, 그 여파로 코드 한 줄 건드리지 않은 하위 클래스가 오동작할 수 있다.
## 2. 상속의 문제를 해결하는 방법: 컴포지션
- 기존 클래스를 확장하는 대신, 새로운 클래스를 만들고 private 필드로 기존 클래스의 인스턴스를 참조하게 하면 된다.
- 기존 클래스가 새로운 클래스의 구성요소로 쓰인다는 뜻에서 이러한 설계를 컴포지션(composition; 구성)이라 한다.
- 새 클래스의 인스턴스 메서드들은 (private 필드를 참조하는) 기존 클래스의 대응하는 메서드를 호출해 그 결과를 반환한다.
- 이 방식을 전달(forwarding)이라 하며, 새 클래스의 메서드들을 전달 메서드(forwarding method)라 부른다.
- 그 결과 새로운 클래스는 기존 클래스의 내부 구현 방식의 영향에서 벗어나며, 심지어 기존 클래스에 새로운 메서드가 추가되더라도 전혀 영향받지 않는다.
- 상속 방식은 구체 클래스를 각각 따로 확장해야 하며, 지원하고 싶은 상위 클래스의 생성자 각각에 대응하는 생성자를 별도로 정의해줘야 한다.
- 컴포지션 방식은 한 번만 구현해두면 어떠한 Set 구현체라도 계측할 수 있으며, 기존 생성자들과도 함께 사용할 수 있다.
- 래퍼 클래스와 Self 문제
- 콜백 프레임워크에서는 자기 자신의 참조를 다른 객체에 넘겨서 다음 호출(콜백)때 사용하도록 한다.
- 내부 객체는 자신을 감싸고 있는 래퍼의 존재를 모르니 대신 자신(this)의 참조를 넘기고, 콜백 때는 래퍼가 아닌 내부 객체를 호출하게 되는데, 이를 SELF 문제라고 한다.
## 3. 상속과 is-a
- 상속은 반드시 하위 클래스가 상위 클래스의 '진짜' 하위 타입인 상황에서만 쓰여야 한다.
- 즉, 클래스 B가 클래스 A와 is-a 관계일 때만 클래스 A를 상속해야 한다.
- 만약, is-a 관계가 아니라면 A는 B의 필수 구성 요소가 아니라 구현하는 방법 중 하나일 뿐이다.
## 4. 상속을 사용할 때 주의할 점
- 컴포지션을 써야할 상황에서 상속을 사용하는 건 내부 구현을 불필요하게 노출하는 것과 같다.
- 클라이언트에서 노출된 내부에 직접 접근할 수 있게 된다.
- 최악의 경우, 클라이언트에서 상위 클래스를 직접 수정하여 하위 클래스의 불변식을 해칠 수도 있다.
- 상속은 상위 클래스의 API를 결함까지도 승계하므로 주의해야 한다.
## 정리
- 상속은 강력하지만 캡슐화를 해친다는 문제가 있다.
- 상속은 상위 클래스와 하위 클래스가 순수한 is-a 관계일 때만 써야 한다.
- is-a 관계일 때도 하위 클래스의 패키지가 상위 클래스와 다르고, 상위 클래스가 확장을 고려해 설계되지 않았을 수도 있기 때문에 안심할 수 없다.
- 상속의 취약점을 피하려면 상속 대신 컴포지션과 전달을 사용한다. 특히 래퍼 클래스로 구현할 적당한 인터페이스가 있다면 더욱 그렇다. 래퍼 클래스는 하위 클래스보다 견고하고 강력하다.
728x90
반응형
'dev book > Effective Java' 카테고리의 다른 글
[Effective Java/아이템20] 추상 클래스보다는 인터페이스를 우선하라. (0) | 2021.12.07 |
---|---|
[Effective Java/아이템19] 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라. (0) | 2021.11.19 |
[Effective Java/아이템17] 변경 가능성을 최소화하라. (0) | 2021.10.25 |
[Effective Java/아이템16] public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라. (2) | 2021.10.24 |
[Effective Java/아이템15] 클래스와 멤버의 접근 권한을 최소화하라. (0) | 2021.10.23 |
Written by ner.o
개발자 네로의 개발 일기,
자바를 좋아합니다 !
댓글 개