IT/Programming / / 2023. 4. 27. 09:44

<Effective Java> RULE 17 계승을 위한 설계와 문서를 갖추거나, 그럴 수 없다면 계승을 금지하라

반응형

 

계승을 위한 설계와 문서를 갖춘다?

 재정의 가능 메서드를 내부적으로 어떻게 사용하는지(self-use) 반드시 문서에 남기라는 것이다.

 모든 메서드와 생성자에 대해, 어떤 재정의 기능 메서드를 어떤 순서로 호출...

 좋은 API문서는 메서드가 하는일이 무엇인지 명시해야지 그일을 어떻게 하는지 명시해서는 안된다??

 하위 클래스를 만들기 안전한 클래스가 되려면 문서에 구현 세부사항을 명시해야 한다.

 

클래스 내부동작에 개입할 수 있는 훅(Hooks)을 신중하게 고른 protected메서드 형태로 제공해야 한다.

protected void removeRange(int fromIndex, int toIndex)

Removes from this list all of the elements whose index is between fromIndex
, inclusive, and toIndex, exclusive. Shifts any succeeding elements
 to the left (reduces their index). This call shortens the list by
 (toIndex - fromIndex) elements. (If toIndex==fromIndex,
 this operation has no effect.)
 
_This method is called by the clear operation on this list and its subLists._
 Overriding this method to take advantage of the internals of the list implementation
can substantially _improve the performance of the clear operation on __this list
 and its subLists.
_
이 메서드는 this 리스트와 그 부분 리스트에 clear를 적용하면 호출된다.
이 메서드를 재정의 하면 this리스트와 그 부분 리스트에 대한 clear연산 성능을 많이 올릴 수 있다.

This implementation gets a list iterator positioned before fromIndex,
 and repeatedly calls ListIterator.next followed by ListIterator.remove
until the entire range has been removed.

_Note: if ListIterator.remove
requires linear time, this implementation requires quadratic time.
_수행시간 복잡도가 선형적일경우, 구현의 수행시간은 제곱에 비례하게 된다.

Parameters:
fromIndex - index of first element to be removed
toIndex - index after last element to be removed
 

계승을 위해 설계한 클래스를 테스트할 유일한 방법은 하위 클래스를 직접 만들어 보는 것이다.

 

널리 사용될 클래스를 계승에 맞게 설계할 때는, 문서에 명시한 내부 호출 패턴뿐 아니라 메서드와 필드를 protected로 선언하는 과정에 함축된 구현 관련 결정들을 영원히 고수해야 한다는 점을 기억해야 한다. 그렇게 되면 다음 릴리스의 성능이나 기능을 개선하기 어려워 진다.

 

계승을 허용할 때 반드시 따라야할 제약사항

 생성자는 직접적이건 간접적이건 재정의 가능 메서드를 호출해서는 안된다는 것이다.

 상위 클래스생성자는 하위 클래스 생성자보다 먼저 실행 되므로 하위 클래스에서 재정의한 메서드는 하위 클래스 생성자가 실행되기 전에 호출 됨.

Public class Super{
    //생성자가 재정의 가능 메서드를 호출하는 잘못된 사례
    public Super(){
        overrideMethod();
    }
    public void overrideMethod(){
    }
}

public final class SubClass extends Super{
    private final Date date;
    
    SubClass(){
        date = new Date();
    }
    
    //상위 클래스 생성자가 호출하는 재정의 메서드
    @Override public void overrideMethod(){
        sysout(date);
    }
    
    main(){
        SubClass sub = new SubClass();
        sub.overrideMethod();
    }
}

CONSOLE
----------
null
----------
 

SubClass 생성자가 호출되어 date필드를 초기화 하기전에 Super생성자가 overrideMethod메서드를 호출해 버리기 때문이다.

 

결론 : 계승에 맞게 설계하고 문서화 하지 않은 클래스에 대한 하위 클래스는 만들지 않아야 한다.

 

하위 클래스 생성을 금지하는 방법

 클래스를 Final로 선언하는 법

 모든 생성자를 private나 package-private로 선언하고 생성자대신 public 정적 팩토리 메서드를 추가.

 (클래스나 패키지 내부에서는 하위 클래스를 만들어 쓸 수 있다.)

 

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유