네로개발일기

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

# 상속보다는 컴포지션을 사용하라.

- 상속이 안전할 때
* 상위 클래스와 하위 클래스를 모두 같은 프로그래머가 통제하는 패키지 안에서 사용한다.
* 확장할 목적으로 설계되었고 문서화도 잘 되어있다.
- 상속이 안전하지 않을 때
* 일반적인 구체 클래스를 패키지 경계를 넘어 다른 패키지의 구체 클래스를 상속하는 일은 위험하다. 이때 '상속'은 다른 패키지의 구체 클래스를 확장하는 '구현 상속'을 의미하며, 인터페이스의 상속과는 무관하다.

# 하위 클래스가 깨지기 쉬운 이유

## 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
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !