0. Introduction
- [Item 18] 상속보다는 컴포지션을 사용하라 에서 상속이 발생할 수 있는 문제점과 이를 해결할 수 있는 방법인 Composition 기법에 대해 알아보았습니다.
- 해당 파트에서는 이런 상속에 의해 발생할 수 있는 문제점을 어느 정도 줄여줄 수 있는 방침인 문서화에 대해 이야기합니다.
<aside>
💡
과정 목표
- 상속을 해야 하는 경우에 대해 필요한 과정 에 대해 이해합니다.
- 이런 과정이 수행되지 않은 클래스에 대한 취급에 대해 이해합니다.
</aside>
1. 상속용 클래스에 대한 문서화
- 이전 아이템에서는 상속을 생각하지 않고, 상속 시에 주의해야 하는 문서도 없이 설계한 외부 클래스에 대해 이 클래스를 상속했을 때에 발생할 수 있는 위험에 대해 알아보았습니다.
- 이때 외부는 시스템을 개발하는 개발자가 통제할 수 없고, 릴리즈때 마다 변경될 수 있는 상황에 놓인 상태를 이야기합니다.
- 이런 문제점을 어느 정소 해소하기 위해서는 클래스를 상속용으로 설계할 것인지에 대해 고심하는 것은 물론이고, 클래스를 상속할 때 주의해야할 점을 명확히 문서로 남겨놓아야 합니다.
- 이번 아이템에서는 클래스를 상속할 때 주의해야할 점을 문서로 어떻게 남기고, 문서화 되지 않은 클래스에 대해서 어떻게 취급할 것인지에 대해 알아보겠습니다.
1-1. 상속용 클래스에 대한 문서
- 상속용 클래스는 재정의(Overriding)할 수 있는 메서드들을 내부적으로 어떻게 이용하는지에 대해 문서로 남겨야 합니다.
- 이는 이전 아이템에서 설명했던 자기 사용 메서드에 대해 확실히 문서화해 놓아야 한다는 것입니다. 예를 들자면 이전
Set
과 같은 Collection Framework에서의 addAll()
메서드와 같은 것입니다.
- 이때, 재정의 가능한 메서드라는 것은 어떤 메서드의 접근 제한자가
public
또는 protected
이고, final
이 아닌 모든 메서드를 의미합니다.
- 클래스의 API로 공개된 메서드는 그 내부 구현이 클래스 자신의 다른 메서드를 호출할 수도 있습니다. 이때 호출하는 메서드가 재정의 가능한 메서드라면 그 사실을 호출하는 메서드의 API 설명에 적시해야 합니다.
addAll()
메서드는 내부 구현에서 add()
메서드를 사용합니다. 이 말은 addAll()
이 add()
메서드를 호출하느 형식으로 구현된다는 것을 설명에 적시해야 한다는 것입니다.
- 덧붙여, 어떤 순서로 호출하는지, 각각의 호출 결과가 이어지는 처리에 대해 어떤 영향을 주는지도 적시해야 합니다.
- 더 넓은 의미로 보면 재정의 가능한 메서드를 호출할 수 있는 모든 상황에 대해 문서로 남겨놓아야 합니다.
- 이에 대한 책의 예시를 보면서 알아봅시다.
public boolean remove(Object o)
주어진 원소가 이 컬렉션 안에 있다면 그 인스턴스를 하나 제거한다.(선택적 동작) 더 정확히는 이 컬렉션 안에 Object.equals(o, e)
가 참인 원소 e
가 하나 이상 있다면, 그 중 하나를 제거한다. 주어진 원소가 컬렉션 안에 있었다면 true
를 반환한다.
Implementation Requirements: 이 메서드는 컬렉션을 순회하며 주어진 원소를 찾도록 구현되었다. 주어진 원소를 찾으면 반복자의 remove()
메서드를 사용해 컬렉션에서 제거한다.
이 컬렉션이 주어진 객체를 갖고 있으나, 이 컬렉션의 iterator()
메서드가 반환한 반복자가 remove()
메서드를 구현하지 않았다면 UnsupportedOperationException
을 던진다.
- 이때 Implementation Requirements는 그 메서드의 내부적 동작 방식을 설명하는 곳입니다.
- 위의 설명에 따르면 만약 저희가 해당 컬렉션의 반복자(
Iterator
)를 반환하는 메서드인 iterator()
를 재정의 한다면 remove()
메서드에 영향을 줄 수 있다는 것을 명시하고 있습니다.
- 재정의 하여 반환하는
iterator
객체가 remove()
메서드를 구현하지 않았다면 예외를 던진다는 것이 영향을 설명하는 부분입니다.
- 또한 위의
iterator()
메서드는 재정의가 가능한 메서드입니다. 역시나 그 사실을 문서에서 적시하고 있는 것을 확인할 수 있습니다.
- 동시에 어떤 순서로 호출하는지도 명시되어 있는데,
iterator()
메서드를 먼저 사용한 뒤, 메서드가 반환한 반복자가 조건과 일치한다면 Iterator
의 remove()
메서드를 호출한다고 명시된 것입니다.