잘 설계된 컴포넌트는 모든 내부 구현을 완벽히 숨겨, 구현과 API를 깔끔히 분리한다. 오직 API를 통해서만 다른 컴포넌트와 소통하며 서로의 내부 동작방식에는 전혀 개의치 않는다.정보 은닉, 혹은 캡슐화라고 하는 이 개념은 소프트웨어 설계의 근간이 되는 원리이다.
자바는 정보 은닉의 핵심은 접근 제한자이다. 기본 원칙은 간단하다 모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 한다. public 클래스의 인스턴스 필드는 되도록 public 이 아니어야한다. 그렇지 않은 경우 필드와 관련된 모든 것은 불변식을 보장할 수 없게 된다는 뜻이다. public 가변 필드를 갖는 클래스는 일반적으로 스레드 안전하지 않다. (필드가 수정될 때 ex.락 획득 같은.. 다른 작업을 할 수 없게된다.)
이러한 문제는 정적필드에서도 마찬가지이나 예외가 하나 있다. 상수인 public static final 필드이다. 이런 필드는 반드시 기본 타입 값이나 불변 객체를 참조해야 한다. 가변 객체를 참조한다면 참조된 객체 자체는 수정될 수 있으니 끔찍한 결과를 초래할 수도 있는 것이다.
따라서 클래스에서 public static final 배열 필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공해서는 안된다. 예컨데 다음 코드에서는 보안 허점이 존재한다.
// 보안 허점이 숨어 있다.
public static final Thing[] VALUES = {...};
해결책은 두가지이다. 첫번째 방법은 앞 코드의 public 배열을 private으로 만들고 public 불변 리스트를 추가하는 것이다.
private static final Thing[] PRIAVATE_VALUES = {...};
public static final List<Thing> VALUES =
Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
// Collections.unmodifiableList -> 읽기 전용 뷰를 만들어 줌
두번째는 배열을 pirvate으로 만드록 그 복사본을 반환하는 public 메서드를 추가하는 방법이다. (방어적 복사)
private static final Thing[] PRIAVATE_VALUES = {...};
public static final Thing[] values() {
return PRIVATE_VALUES.clone();
}
이 방식은 배열 구조 변경이나 배열교체 순서 변경을 차단하지만 요소 객체의 가변성 까지는 막아주지 못한다. 단지 내부 배열을 외부에서 망가뜨리지 못하게 하려는 거다.
프로그램 요소의 접근성은 가능한 최소한으로 하자. 꼭 필요한 것만 골라 최소한의 public API를 설계하자. 그외에는 클래스, 인터페이스, 멤버가 의도치 않게 API로 공개되는 일은 없어야한다. public 클래스 상수용 public static final 필드 외에는 어떠한 public 필드도 가져서는 안된다. public static final 필드가 참조하는 객체가 불변인지 확인하라.