2 minute read

item15 클래스와 멤버의 접근 권한을 최소화하라

  • 잘 설계된 컴포넌트는 클래스 내부 데이터와 내부 구현 정보를 외부 컴포넌트로부터 얼마나 잘 숨겼는지에 의해 결정된다.
    • 모든 내부구현을 완벽히 숨겨, 구현과 API를 분리한다.
    • API를 통해서만 타 컴포넌트와 소통하며 서로의 내부 동작 방식을 알지 못한다.
    • -> 정보 은닉 / 캡슐화

정보 은닉의 장점

  • 시스템 개발 속도 ↑
    • 여러가지 컴포넌트를 병렬로 개발할 수 있다.
  • 시스템 관리 비용 ↓
    • 컴포넌트를 빨리 파악하여 디버깅 할 수 있고, 다른 컴포넌트로 교체하는 부담도 적다.
  • 성능 최적화에 도움
    • 다른 컴포넌트에 영향을 주지 않고 타겟이 되는 컴포넌트만 최적화가 가능하다.
  • 소프트웨어의 재사용성 ↑
    • 기존의 인터페이스를 그대로 사용할 수 있다.
  • 큰 시스템을 제작하는 난이도를 낮춰준다.

모든 클래스와 멤버의 접근성을 가능한 한 좁히자. 소프트웨어가 올바로 동작하는 한 가장 낮은 접근 수준을 부여해야 한다.

  • Java에서는 이를 위해 다양한 장치를 사용하는데, 접근 제한자가 그 대표적 예시이다.

Java의 접근 제한자

  • private : 멤버를 선언한 톱레벨 클래스에서만 접근 가능
  • package-private(=default) : 멤버가 소속된 패키지 안의 모든 클래스에서 접근 가능하다.
  • protected : package-private의 접근 범위를 포함하며, 이 멤버를 선언한 클래스의 하위 클래스에서도 접근 가능하다.
  • public : 모든 곳에서 접근 가능하다.

톱레벨 수준(파일명 = 클래스명)이 같은 수준에서의 접근제한자는 publicpackage-private만 사용 할 수 있다.

  • public으로 선언하는 경우 공개 API로 사용
    • 하위호환을 평생 신경써야 한다.
    • public 클래스의 인스턴스 필드는 되도록 public이 아니어야 한다.(아이템 16)
    • 필요에 따라 public static final 상수 필드는 공개해도 좋다. (추상 개념 구성요소로서의 상수)
    • public 가변 필드를 가지는 클래스는 Thread-safe하지 않다.
// 해결책 1 : 배열을 private로 만들고, public 불변 리스트를 추가한다.

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


// 해결책 2: 배열을 private로 만들고 이 복사본을 반환하게 한다.

private static final Thing[] PRIVATE_VALUES = {...};  
public static final Thing[] values() {  
  return PRIVATE_VALUES.clone(); // 방어적 복사
}
  • package-private로 사용하는 경우 해당 패키지 안에서만 사용 가능
    • 다음 릴리즈에 내부로직을 변경해도 괜찮다.
    • public일 필요가 없는 클래스의 접근 수준을 default 클래스로 좁혀야 한다.
  • private, package-private : 해당 클래스의 구현에 해당하므로 공개 API에 영향을 주지 않는다.
    •  처음에 모든 멤버는 private으로 -> 필요시 package-private하게 변경
  • 메서드를 재정의 할 경우에는 접근 수준을 상위 클래스에서보다 좁게 설정 할 수 없다. (리스코프 치환 원칙)
    • 인터페이스의 경우 메서드를 모두 public으로 해야 한다.

Java Modules (Java 9~)

  • 패키지는 클래스의 묶음인 것 처럼, 모듈(Modules)은 패키지의 묶음이다.
  • 모듈은 자신이 속하는 패키지 중 공개(export) 할 것을 선언한다.
  • 정의하기 위해 3가지 정보를 작성해야 한다.
  1. module 명 / 명칭
  2. 공개할 package (export)
    • 공개한 package의 public 접근제한자만 외부에서 접근할 수 있다.
    • export하지 않았다면, public 접근제한자가 있어도 접근을 할 수 없다.
  3. module내 package의 클래스를 사용하기 위해 종속되는 package (require)
  • 이 접근 수준은 주의해 사용해야 한다.
  • 모듈의 JAR file을 classpath에 두면 모듈 내 모든 패키지가 모듈이 없는 것처럼 행동한다. 즉, public 클래스 내 모든 public 혹은 protected 멤버에 접근할 수 있게 하기 때문이다.

Leave a comment