규칙 13 클래스와 멤버의 접근 권한은 최소화하라

규칙 13. 클래스와 멤버의 접근 권한은 최소화하라

잘 설계된 모듈과 그렇지 못한 모듈을 구별 짓는 가장 중요한 속성 하나는 모듈 내부의 데이터를 비롯한 구현 세부사항을 다른 모듈에 잘 감추느냐의 여부에 달려있다.

잘 설계된 모듈은 구현 세부사항을 전부 API 뒤쪽에 감춘다. 모듈은 이 API를 통해서만 서로 통신하며, 각자 내부적으로 무슨 짓을 하는지는 신경쓰지 않는다. 이 개념은 정보 은닉(Information hiding) 또는 캡슐화(encapsulation)라는 용어로 알려져 있으며, 소프트웨어 설계의 기본적인 원칙 가운데 하나다.

정보 은닉(Information Hiding)의 장점

  • 의존성 최소화

    각자 개별적으로 개발하고, 시험하고, 최적화하고, 이해하고, 변경할 수 있도록 한다는 사실에 기초한다. 그렇게 되면 시스템 개발 속도가 올라가는데, 각각의 모듈을 병렬적으로 개발할 수 있기 때문이다.

  • 각 모듈의 병렬 개발 가능
  • 유지 보수의 용이

    유지보수의 부담도 낮아지는데, 모듈 각각을 좀 더 빨리 이해할 수 있을 뿐 아니라 다른 모듈에 영향을 끼칠 걱정 없이 디버깅을 진행할 수 있기 때문이다.

  • 효과적인 성능 튜닝 용이

    정보 은닉의 원칙이 좋은 성능을 자동적으로 보장하는 것은 아니지만, 효과적인 성능 튜닝(tunning)을 가능하게 하는 것은 사실이다. 시스템이 완성된 다음에 어떤 모듈이 성능 문제를 일으키는지 프로파일링(profiling)하기 용이하기 떄문이다.

  • 재사용성 향상
  • 대규모 개발 RISK 감소

원칙

각 클래스와 멤버는 가능한 한 접근 불가능하도록 만들어라

개발 중인 소프트웨어의 정상적인 동작을 보증하는 한도 내에서 가장 낮은 접근 권한을 설정하라

최상위 레벨 클래스와 인터페이스에 부여할 수 있는 접근 권한은 package-private과 public 두가지다.
package-private으로 선언하면 API의 일부가 아니라 구현 세부사항이 됨으로 다음번 릴리즈에 클라이언트 코드를 깨뜨릴 걱정없이 변경하거나 삭제가 가능하다. 하지만 Public으로 선언하게 되면 호환성을 보장하기 위해 해당 개체를 계속 지원해야 한다.

필드메서드, 중첩 클래스(nested class), 중첩 인터페이스(nested interface) 같은 멤버의 접근 권한은 아래의 네 개 중 하나로 설정할 수 있다.

  • private - private으로 선언된 멤버는 선언된 최상위 레벨 클래스 내부에서만 접근 가능 하다.
  • package-private - package-private로 선언된 멤버는 같은 패키지 내의 아무 클래스나 사용할 수 있다. 기본 접근 권한(defualt access)로 알려져 있는데, 멤버를 선언 할 떄 아무런 접근 권한 수정자(access modifier)도 붙이지 않으면, 이 권한이 주어진다.
  • protected - protected로 선언된 멤버는 선언된 클래스 및 그 하위 클래스만 사용할 수 있다.
  • public - public은 어디에서나 사용 가능하다.

객체 필드(instance field)는 절대로 Public으로 선언하지 마라

비 final 필드나 변경 가능한 객체에 대해 final 참조 필드를 public으로 선언하면 필드에 저장될 값을 제한할 수 없게 된다. 따라서 그 필드에 관계된 불변식을 강제할 수 없다. 필드가 변경될 떄 특정한 동작이 실행되도록 할수도 없으므로 변경가능 public 필드를 가진 클래스는 다중 스레드에 안전하지 않다.

길이가 0이 아닌 배열은 언제나 변경 가능하므로, public static final 배열 필드를 두거나, 배열 필드를 반환하는 접근자를 정의하면 안된다

//보안 문제를 초례할 수 있는 코드
public static final Thing[] VALUES = { ... };

이 문제를 고치는 두가지 방법

private static final Thing[] PRIVATE_VALUES = { ... };
public static final List<Thing> VALUES = Collections.unmodifiableList(Array.asList(PRIVATE_VALUES));
private static final Thing[] PRIVATE_VALUES = { ... };
public static final Thing[] values() { return PRIVATE_VALUES.clone(); }

클라이언트가 어떤 작업을 해야하는지에 따라 두가지 방법중 하나를 선택하면 된다.

출처 : 조슈아 블로크, 『 Effective Java 2/E』, 이병준 옮김, 인사이트(2014.9.1), 규칙13 인용.