어설프게 설계된 컴포넌트와 잘 설계된 컴포넌트의 가장 큰 차이는 클래스 내부 데이터와 내부 구현정보를 외부 컴포넌트로부터 얼마나 잘 숨겼느냐 이다.
잘 설계된 컴포넌트는 모든 내부구현을 완벽히 숨겨, 구현과 API를 깔끔히 분리한다.
정보은닉의 기본원칙은 모든 클래스와 맴버의 접근성을 가능한 한 좁혀야 한다 이다. 달리 말하자면 소프트웨어가 올바로 동작하는 한 항상 가장 낮은 접근수준을 부여해야 한다는 뜻.
톱레벨 클래스와 인터페이스에 부여할 수 있는 접근수준은 아래 두가지이다.
패키지 외부에서 쓸 이유가 없다면 package-private으로 선언하자. 그러면 이들은 API가 아닌 내부 구현이 되어 언제든 수정할 수 있지만, API 들은 하위호환을 위해 수정, 교체, 제거에 제한을 받게 된다.
이 접근수준들을 잘 이용하여 가장 낮은 접근 수준들을 유지하려면
1. 해당 클래스의 공개 API를 세심하게 설계한 후
2. 그 외의 모든 멤버는 private으로 만든다.
3. 그 다음 오직 같은 패키지의 다른 클래스가 접근해야 하는 멤버에 한해서 private -> package-private으로 풀어주자.
그리고 맴버들의 접근수준에 대해 유의해야될 점들을 아래 나열해 보았다.
public 클래스에서는 멤버의 접근 수준을 package-private에서 protected로 바꾸는 순간 그 멤버는 public API 이므로 protected 멤버의 수는 적을수록 좋다.
상위이 클래스의 메서드를 override 할 때는 그 접근수준을 상위 클래스에서보다 좁게 설정할 수 없다.
이 제약은 상위 클래스의 인스턴스는 하위 클래스의 인스턴스로 대체해 사용할 수 있어야 한다는 원치긍ㄹ 지키기 위해 필요하다.
테스트를 위해 멤버의 접근 범위를 적당한 수준에서(privagte -> package-private) 정도로 늘리는 건 가능하지만, 그 이상을 허용해버리면 API가 만들어지기 때문에 유의해야 한다. 더 늘리기 보단 테스트코드를 테스트 대상과 같은 패키지에 두어 package-private으로도 접근 가능하도록 하자.
public 클래스의 인스턴스 필드는 되도록 public 이 아니어야 한다.
필드가 가변 객체를 참조하거나, final이 아닌 인스턴스 필드를 public 으로 선언하면 그 필드에 담을 수 있는 값을 제한할 힘을 잃게 된다. 추가적으로, 필드가 수정될 때 다른 작업을 할 수 없게 되므로 public 가변 필드를 갖는 클래스는 일반적으로 스레드 안전하지 않다.
해당 클래스가 표현하는 추상 개념을 완성하는데 꼭 필요한 구성요소로써의 상수라면 public static final 필드로 공개해도 좋다.
길이가 0이 아닌 배열은 모두 변경 가능하다. 클래스에서 public static final 배열 필드를 두거나 이 필드를 반환하는 접근제 메서드를 제공해서는 안된다.
해결책은
private static final Thing[] PRIVATE_VALUES = {...};
public static final List<Thing> VALUES =
Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
private static final Thing[] PRIVATE_VALUES = {...};
public static final Thing[] values() {
return PRIVATE_VALUES.clone();
}
자바 9 부터는 모듈 시스템이라는 개념이 도입되면서 패키지 안에서 공개될 수 있는 접근 범위를 새로 정의할 수 있다. 패키지가 클래스의 묶음이듯, 모듈은 패키지들의 묶음으로, 모듈은 자신이 속하는 패키지 중 공개(export)할 것들을 module-info.java
에 선언한다.
이런 패키지 보다 한단계 높은 모듈 안에서 정의할 수 있는 접근수준은, public 과 protected 수준과 같으나, 그 효과가 모듈 내부로 한정되는 변종으로 작동하는 접근 수준이 생긴다. 하지만 이는 모듈을 어느 경로에 두었는지, 패키지들을 어떻게 묶었는지, 의존성을 어떻게 명시했는지에 따라 모듈의 정의가 달라지기 때문에, 나중에 필요한 순간이 오면 더 자세하게 정리하도록 하자.