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

wisdom·2022년 8월 18일
0

Effetctive Java

목록 보기
19/80
post-thumbnail

✔️ 상속을 고려한 문서화와 설계

문서화

상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지 문서로 남겨야 한다.

재정의 가능 메서드를 호출할 수 있는 모든 상황을 문서로 남겨야 한다.

💡 재정의 가능 메서드 : public과 protected 메서드 중 final이 아닌 모든 메서드.

@implSpec 태그

  • 메서드 주석에 붙여주면 자바도 도구가 "Implementation Requirements"로 시작하는, 메서드의 내부 동작 방식을 설명하는 절을 생성해준다.
  • 이 태그를 활성화하려면 명령줄 매개변수로 -tag "implSpec:a:Implementation Requirents:"를 지정해주면 된다.

protected 메서드 및 필드

클래스의 내부 동작 과정 중간에 끼어들 수 있는 훅을 잘 선별하여 protected 메서드 형태로 공개해야 할 수도 있다.
드물게는 protected 필드도 공개해야 할 수도 있다.

그렇다면 어떤 메서드를 protected로 노출해야 할지는 어떻게 결정할까?

안타깝게도 마법은 없다. 상속용 클래스를 시험하는 방법은 직접 하위 클래스를 만들어보는 것이 유일하다.

저자의 경험상 이러한 검증은 하위 클래스 3개 정도를 만들어보는 것이 적당하다고 한다.


✔️ 상속을 허용하는 클래스의 제약사항

1️⃣ 상속용 클래스의 생성자는 직접적으로든 간접적으로든 재정의 가능 메서드를 호출해서는 안 된다.

  • 상위 클래스의 생성자가 하위 클래스의 생성자보다 먼저 실행되므로, 하위 클래스에서 재정의한 메서드가 하위 클래스의 생성자보다 먼저 호출된다. 이때 그 재정의한 메서드가 하위 클래스의 생성자에서 초기화하는 값을 의존한다면 의도대로 동작하지 않을 것이다.
  • private, final, static 메서드는 재정의가 불가능하니 생성자에서 안심하고 호출해도 된다.

2️⃣ clonereadObject 모두 직접적으로든 간접적으로든 재정의 가능 메서드를 호출해서는 안 된다.

  • CloneableSerializable 인터페이스 둘 중 하나라도 구현한 클래스를 상속할 수 있게 설계하는 것은 일반적으로 좋지 않은 생각이다. 이 인터페이스를 하위 클래스에서 구현하도록 하는 특별한 방법은 아이템 13과 86에서 설명한다.

3️⃣ Serializable을 구현한 상속용 클래스가 readResolvewriteReplace 메서드를 갖는다면 이 메서드들은 private이 아닌 protected로 선언해야 한다.

  • private으로 선언한다면 하위 클래스에서 무시되기 때문이다.

✔️ 일반적인 구체 클래스의 상속 금지

일반적인 구체 클래서의 경우, 전통적으로 final도 아니고 상속용으로 설계되거나 문서화되지도 않았다.
이로 인해, 클래스에 변화가 생길 때마다 하위 클래스를 오동작하게 만들 수 있다.

이 문제를 해결하는 가장 좋은 방법은 상속용으로 설계하지 않은 클래스는 상속을 금지하는 것이다.
상속을 금지하는 방법은 2가지다.

1️⃣ 클래스를 final로 선언한다. (더 쉬운 방법)

2️⃣ 모든 생성자를 private이나 package-private으로 선언하고, public 정적 팩터리를 만들어준다.



📌 핵심 정리

  • 상속용 클래스는 내부에서 스스로를 어떻게 사용하는지 모두 문서로 남겨야 하며, 문서화한 것은 반드시 지켜야 한다.
  • 다른 이가 효율 좋은 하위 클래스를 만들 수 있도록 일부 메서드를 protected로 제공해야 할 수도 있다.
  • 클래스를 확장해야 할 명확한 이유가 없다면, 상속을 금지하는 것이 낫다.
  • 상속을 금지하려면, 클래스를 final로 선언하거나 생성자 모두를 외부에서 접근할 수 없도록 만들면 된다.
profile
백엔드 개발자

0개의 댓글