Item 19. 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라

심규환·2022년 1월 28일
0

Effective Java

목록 보기
18/29

상속을 고려한 설계와 문서화란 무엇일까?
우선, 메서드를 재정의하면 내부적으로 어떤 역할을 하는지 정확히 정리하여 문서를 남겨야 한다. 달리 말하면 상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지 문서로 남겨야 한다. 클래스의 API로 공개된 메서드는 메서드 자신에서 다른 메서드를 호출할 수 있다. 이 호출되는 메서드가 재정의 메서드라면, 그 사실을 호출하는 메서드의 API 설명에 자세히 적어야 한다. 어떤 역할을 하는지 어떤 영향을 주는지 담아야 한다. 요약하자면 이 재정의 메서드를 호출할 수 있는 모든 상황을 문서로 남겨야 한다.

API 문서의 메서드 설명 끝에서 종종 "Implementation Requirements"로 시작하는 절을 볼 수 있는데, 이는 그 메서드의 내부 동작 방식을 설명하는 곳이다.
메서드 주석에 @implSepc 태그를 붙여주면 자바독 도구가 생성해준다.

다음은 java.util.AbstractCollection의 내용이다.

public boolean remove(Object o)
주어진 원소가 이 컬렉션 안에 있다면 그 인스턴스를 하나 제거한다(선택적 동작). 더 정확하게 말하면 이 컬렉션 안에 'Object.equals(o, e)가 참인 원소' e가 하나 이상 있다면 그 중 하나를 제거한다. 주어진 원소가 컬렉션 안에 있었다면(즉, 호출 결과 이 컬렉션이 변경됐다면) true를 반환한다.
Implementation Requirements: 이 메서드는 컬렉션을 순회하며 주어진 원소를 찾도록 구현되었다. 주어진 원소를 찾으면 반복자의 remove 메서드를 사용해 컬렉션에서 제거한다. 이 컬렉션이 주어진 객체를 갖고 있으나, 이 컬렉션의 iterator 메서드가 반환한 반복자가 remove 메서드를 구현하지 않았다면 UnsupportedOperationExceptioin을 던지니 주의하자.

이는 iterator의 메서드의 remove를 사용하기 때문에 메서드를 재정의하면 영향을 준다는 것을 확인할 수 있다.

이처럼 내부 매커니즘을 문서로 남기는 것만이 상속을 위한 설계의 전부는 아니다.
효율적인 하위 클래스를 큰 어려움 없이 만들려면 클래스의 내부 동작 과정 준간에 끼어들 수 있는 훅(hook)을 잘 선별하여 protected 메서드 형태로 공개해야 할 수도 있다.
이게 무슨 말일까? 쉽게 말해서 하나의 List가 있다고 생각해보자. List 안의 인덱스 하나를 지우는 메서드를 remove라 하자. 그런데 상위 클래스에서 remove를 도와 인덱스를 지우는 더 효율적인 메서드를 추가했다면 하위 클래스에서는 선택적으로 이를 재정의해서 더 효율적으로 할지 말지 정할 수 있다.
이러한 메서드를 proteced로 설정하라는 것이다. 그러면 어떤 메서드를 protected로 노출해야 할까? 유일한 방법은 하위 클래스를 최소 3개를 만들어서 필요한 메서드가 무엇인지 확인해보는 것이다.
만약 3번 만들때까지 안쓰이는데 protected-> private로 변경하는 것이 좋다.
상속용으로 설계한 클래스는 배포 전에 반드시 하위 클래스를 만들어 검증해야 한다.

상속용 클래스 생성자는 직접적이든 간접적으로든 재정의 가능 메서드를 호출해서는 안된다.
이 규칙을 어기면 오동작할 확률이 높다. 만약 상위 클래스의 생성자에서 재정의 메서드를 호출한다면, 그리고 하위 클래스에서 재정의 메서드를 호출해서 하위 클래스의 생성자에서 초기화하는 인스턴스 값을 사용한다면?
상위 클래스 생성자가 먼저 만들어지고 재정의 메서드를 호출하게 되고 아직 생성되지 않은 인스턴스 값을 호출해서 NullPointException을 낳게 된다.
이와 마찬가지로 Cloneable과 Serializable 인터페이스도 같은 이유로 재정의 메서드를 호출해서는 안 된다.

클래스를 상속용으로 설계하려면 엄청난 노력이 들고 신경써야 할게 많다는 것을 알았다. 상속용이 아닌 그냥 구체 클래스는 어떡해야 할까? 가능하다면 상속용으로 설계되지 않은 클래스라면 상속을 금지하는 것이 좋다.

profile
장생농씬가?

0개의 댓글