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

문법식·2022년 5월 8일
0

Effective Java 3/E

목록 보기
19/52

상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지(자기사용) 문서로 남겨야 한다. 클래스의 API로 공개된 메서드에서 클래스 자신의 또 다른 메서드를 호출할 수도 있다. 그런데 마침 호출되는 메서드가 재정의 가능(publicproetected 메서드 중 final이 아닌 모든 메서드를 뜻한다.) 메서드라면 그 사실을 호출하는 메서더의 API에 설명을 적어야 한다. 덧붙여서 어떤 순서로 호출하는지, 각각의 호출 결과가 이어지는 처리에 어떤 영향을 주는지도 적어야 한다.
API 문서의 메서드 설명 끝에서 종종 Implementation Requirements로 시작하는 절을 볼 수 있는데, 그 메서드의 내부 동작 방식을 설명하는 곳이다. 이 절은 메서드 주석에 @implSpec 태그를 붙여주면 자바독 도구가 생성해준다.

효율적인 하위 클래스를 큰 어려움 없이 만들 수 있게 하려면 클래스의 내부 동작 과정 중간에 끼어들 수 있는 훅(HOOK)을 잘 선별하여 protected 메서드 형태로 공개해야 할 수도 있다. List 구현체의 최종 사용자는 removeRange 메서드에 관심이 없다. 하지만 이 메서드를 공개한 이유는 단지 하위 클래스에서 부분리스트의 clear메서드를 고성능으로 만들기 쉽게 하기 위해서이다.

상속용 클래스를 설계할 때 어떤 메서드를 protecetd로 노출해야 할지의 정하는 법칙은 없다. 심사숙고해서 잘 예측해본 다음, 실제 하위 클래스를 만들어 시험해보는 것이 최선이다. protected 메서드 하나하나가 내부 구현에 해당하므로 그 수는 가능한 적어야 한다.한편으로 너무 적게 노출해서 상속으로 얻는 이점마저 없애지 않도록 주의해야 한다. 상속용 클래스를 시험하는 방법은 직접 하위 클래스를 만들어 보는 것이 '유일'하다. 꼭 필요한 protected 멤버를 놓쳤다면 하위 클래스를 작성할 때 그 빈 자리를 확실히 느낄 수 있다. 거꾸로, 하위 클래스를 여러 개 만들 때까지 전혀 쓰이지 않는 protected 멤버는 사실 private이었어야 할 가능성이 크다. 필자 경험상 이러한 검증에는 하위 클래스 3개 정도가 적당하다고 한다. 그리고 이중 하나 이상은 제 3자가 작성해봐야 한다.

널리 쓰일 클래스를 상속용으로 설계한다면 내가 문서화한 내부 사용 패턴과, protected 메서드와 필드를 구현하면서 선택한 결정에 영원히 책임져야함을 잘 알아야 한다. 이 결정들이 그 클래스의 성능과 기능에 영원한 족쇄가 될 수 있다. 그러니 상속용으로 설계한 클래스는 배포 전에 반드시 하위 클래스를 만들어 검증해야 한다.

상속용 생성자는 직접적으로 간접적으로 재정의 가능 메서드를 호출해서는 안 된다. 이 규칙을 어기면 프로그램이 오작동할 것이다. 상위 클래스의 생성자가 하위 클래스의 생성자보다 먼저 실행되므로 하위 클래스에 재정의한 메서드가 하위 클래스의 생성자보다 먼저 호출된다. 이때 그 재정의한 메서드가 하위 클래스의 생성자에서 초기화하는 값에 의존한다면 의도대로 동작하지 않을 것이다. ClonealbeSerializable 인터페이스는 상속용 설계를 더 어렵게 한다. 둘 중 하나라도 구현한 클래스를 상속할 수 있게 설계하는 것은 일반적으로 좋지 않은 생각이다. clonereadObject 메서드는 생성자와 비슷한 효과를 낸다. 따라서 이들도 위에서 말한 생성자의 제약을 따라야 한다. 마지막으로 Serializable을 구현한 상속용 클래스가 readResolvewriteReplace메서드를 갖는다면 이 메서드들은 private이 아닌 protected로 선언해야 한다.

상속용으로 설계하지 않는 클래스는 상속을 금지하는 것이 좋다. 상속을 금지하는 방법은 두 가지다. 클래스를 final로 선언하거나, 모든 생성자를 private이나 package-private으로 선언하고 public 정적 팩터리를 만들어주는 방법이다.

클래스의 동작을 유지하면서 재정의 가능 메서드를 사용하는 코드를 제거할 수 있는 기계적인 방법이 있다. 먼저 각각의 재정의 가능 메서드는 자신의 본문 코드를 private 도우미 메서드로 옮기고, 이 도우미 메서드를 호출하도록 수정한다. 그런 다음 재정의 가능 메서드를 호출하는 ㄷ다른 코드들도 모두 이 도우미 메서드를 직접 호출하도록 수정하면 된다.

profile
백엔드

0개의 댓글