클래스를 안전하게 상속할 수 있도록 문서화를 해놓는 것을 권장하고 있습니다. 특히, 재정의 가능 메서드를 호출할 수 있는 모든 상황을 문서로 남겨야합니다.
문서화할 때 ‘어떻게’ 동작하는지도 설명해야 하는 것이 부작용입니다. 좋은 API 문서는 API가 ‘어떻게’ 동작하는지 아닌 ‘무엇’을 하는지만을 설명해야 하지만, 상속으로 인해 캡슐화를 해치게 되어 내부 구현 방식을 설명해야하기 때문입니다.
자바의 경우 API 문서에서는 Implementation Requirements로 시작하는 절이 있는데, 해당 절은 @implSpec 태그를 붙여주면 자바독 도구가 생성해줍니다.
Javadoc : Java 소스를 문서화 하는 방법, html로 열 수 있습니다.
스위프트의 경우 JavaDoc처럼 따로 문서를 만들 수는 없지만, Xcode에서 다양한 주석 및 마크다운을 이용해 메서드 상단에 명시함으로써 호출하는 곳에서 해당 메서드의 설명을 팝업으로 볼 수 있게 되어있습니다.
hook(hooking) : 함수 호출, 중간에서 가로챈다고 표현한다.
예제에서는 Java의 removeRange
메서드를 예로 들고있습니다.
해당 메서드를 protected로 제공한 이유는 clear
메서드가 아래에 나와있는 removeRange
메서드를 부르기 때문입니다.
protected void removeRange(int fromIndex, int toIndex) {
ListIterator<E> it = listIterator(fromIndex);
for (int i=0, n=toIndex-fromIndex; i<n; i++) {
it.next();
it.remove();
}
}
removeRange
메서드 내부의 it.remove()
가 O(n)이 걸리게 되면 removeRange
는 O(n^2)이 걸리게 됩니다.
따라서 해당 메서드를 호출하는 clear
메서드를 고성능으로 만들기 쉽게 하기 위해 removeRange
메서드를 재정의 할수있도록 제공하는 것입니다.
상속용 클래스의 생성자는 직접적으로든 간접적으로든 재정의 가능 메서드를 호출해서는 안됩니다.
class Super {
init() {
overrideMe()
}
public func overrideMe() { }
}
class Sub: Super {
private var subProperty: String!
init(subProperty: String?) {
self.subProperty = subProperty
}
override func overrideMe() {
print(subProperty.description)
}
}
let sub = Sub(subProperty: nil) // 런타임 에러 발생! Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
sub.overrideMe()
NullPointerException
을 던지게 된다는 것입니다.클래스가 final로 선언되지도 않았고, 상속용으로 설계되거나 문서화도 해놓지 않았을 경우입니다. 이러한 클래스는 수정이 생길때마다 하위 클래스가 오동작 할 수 있는 가능성이 있기 때문에 해결할 수 있는 몇가지 방법들이 있습니다.