# ITEM15 클래스와 멤버의 접근 권한을 최소화하라
# 잘 설계된 컴포넌트의 특징
정보 은닉 혹은 캡슐화
클래스 내부 데이터/구현을 완벽히 숨긴다
모든 클래스의 멤버의 가장 낮은 접근 수준 부
→ 구현과 API 의 분리오직 API 를 통해서만 다른컴포넌트와 소통하며 서로의 내부 동작 방식에는 신경쓰지 않음
# 정보 은닉의 장점
시스템을 구성하는 컴포넌트들을 서로 독립시켜서
- 개발, 테스트, 최적화, 적용, 분석, 수정을 개별적으로 할 수 있게 해준다.
- 시스템 개발속도 향상
- 여러 컴포넌트를 병렬개발
- 시스템 관리비용 절감
- 각 컴포넌트를 더 빨리 파악하여 디버깅 할수 있음
- 다른 컴포넌트로 교체비용이 더 적다
- 성능 최적화에 도움
- 다른 컴포넌트에 영향을 주지 않고 해당 컴포넌트만 최적화 할 수 있기 때문
- 소프트웨어 재사용성
- 외부에 의존하지 않고, 독자적으로 동작하는 컴포넌트는 낯선 환경에서도 유용하게 쓰임
- 큰 시스템을 제작하는 난이도를 낮춤
- 시스템 전체가 완성되지 않아도 개별 컴포넌트 동작을 검증할 수 있기 때문
# 자바가 제공하는 정보 은닉의 장치
접근제어 매커니즘
- 클래스, 인터페이스, 멤버의 접근성을 명시
- 접근성 결정 요소
- 요소가 선언된 위치
- 접근 제한자 (private, protected, public)
- 정보 은닉 핵심
# topLevel 클래스, 인터페이스
public 일 필요가 없는 클래스의 접근 수준을 package-private 톱 레벨 클래스로 좁히자
# package-private
- 해당 패키지 안에서만 이용가능
- API 가 아닌 내부 구현이 되어 언제든 수정 가능
- topLevel 클래스인 이유
- 클래스 안에 private static 으로 중첩시키면, 바깥 클래스에서 접근할 수 있게 된다.
# public
- 공개 API
- 하위 호환을 위해 영원히 관리 해야 함.
# 멤버 접근 수준
멤버
- 필드, 메서드, 중첩클래스, 중첩인터페이스
# private
멤버를 선언한 톱 레벨 클래스에서만 접근 가능
# package-private
멤버가 소속된 패키지 안의 모든 클래스에서 접근 가능
- 접근 제한자를 명시하지 않았을 때 default 접근 수준
- 단 인터페이스의 멤버는 public 이 default 임
# protected
package-private 의 접근 범위를 포함하며, 이 멤버를 선언한 클래스의 하위클래스에서 접근 가능
# public
모든 곳에서 접근 가능
- 클래스의 공개 API 를 세심히 설계한다
- 그 외 모든 멤버는 private 으로 만든다
- 같은 패키지의 다른클래스가 접근해야 하는 멤버에 한하여 package-private 을 쓴다.
- 권한을 풀어주는 일을 자주 하게 된다면 컴포넌트를 더 분해해야 하는 것은 아닌지 다시 고민한다
# private / package-private 멤버
- 모두 해당 클래스의 구현에 해당하므로 보통은 API 에 영향을 주지 않는다.
- Serializable 을 구현한 클래스에서는 의도치 않게 공개 API 가 될 수 있다.
# public 클래스의 protected
- 공개 API, 영원히 지원되야 한다.
- 내부 동작 방식을 API 문서에 적어 사용자에게 공개해야 한다.[19]
- 적을수록 좋다.
# 멤버 접근성을 최소화를 방해하는 제약
상위 클래스의 메서드를 재정의 할 때, 접근수준을 더 좁게 할 수 없다.
- 리스코프치환원칙
- 상위 클래스의 인스턴스는 하위 클래스의 인스턴스로 대체해 사용할 수 있어야 한다
# 테스트 목적의 접근 범위
적당한 수준까지는 넓혀도 괜찮다
- public 클래스의 멤버 private → package-private 까지만 허용
- 테스트 코드를 테스트 대상과 같은 패키지에 두면 package-private 요소에 접근 가능
- 테스트만을 위해 클래스, 인터페이스, 멤버를 공개 API 로 만들어서는 안된다.
# public 클래스의 인스턴스 필드는 되도록 public 이 아니어야 한다.[16]
다음과 같은 조건에, 불변식을 보장할 수 없다. (Thread safe 하지 않다)
- 필드가 가변 객체를 참조할 때
- final 이 아닌 인스턴스 필드를 public 으로 선언할 때
상수
- public static final 필드로 공개해도 좋다.
- 대문자알파벳, _으로 단어 구분[68]
- 기본 타입 값이나, 불변 객체를 참조해야함[17]
# 길이가 0 이 아닌 배열은 모두 변경 가능함
- 클래스에서 public static final 배열필드를 두거나, 이 필드를 반환하는 접근자 메서드를 제공해서는 안된다.
해결책 : 클라이언트가 원하는 방향으로 둘 중 하나를 사용한다.
- 배열을 private 으로 만든다 → public 불변 리스트를 추가 한다.
private static final Thing[] PRIVATE_VALUES = { ... };
public static final List<Thing> VALUES =
Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
1
2
3
2
3
- 배열을 private 으로 만든다 → 복사본을 반환하는 public 메서드를 추가한다 (방어적 복사)
private static final Thing[] PRIVATE_VALUES = { ... };
public static final Thing[] values() {
return PRIVATE_VALUES().clone();
}
1
2
3
4
2
3
4
# 자바9 모듈 시스템 도입
- 패키지들의 묶음
- 패키지 중 export (공개) 할 것들을 (관례상 module-info.java 파일에) 선언함.
- protected / public 멤버라도 export 하지 않으면 모듈 외부에서는 접근 불가
- 클래스를 외부에 공개하지 않으면서도 같은 모듈을 이루는 패키지 사이에서는 자유롭게 공개 가능
- 모듈안의 public 클래스의 public/protected 형태로 사용하는 것은 흔치는 않다.
- 패키지들 사이에서 클래스의 재배치로 해결가능하다
# 모듈의 접근 수준은 주의해서 사용해야 함
모듈의 JAR 파일을 자신의 모듈경로가 아닌 애플리케이션의 classpath 에 두면 ?? 그 모듈 안의 모든 패키지는 마치 모듈이 없는 것처럼 행동함.
- 모듈의 export 여부와 상관없이, public 클래스가 선언한 모든 public 혹은 protected 멤버를 모듈 밖에서도 접근 가능
# 모듈의 장점을 사용하기 위해 할 일
- 패키지를 모듈 단위로 묶는다
- 모듈 선언에 패키지들의 모든 의존성을 명시한다
- 소스 트리를 재배치 한다
- 모듈 안으로 부터 일반 패키지로의 모든 접근에 특별한 조치를 해야 한다.
# JDK 는 모듈의 접근 수준을 적극적으로 활용한 대표적인 예
- 자바 라이브러리에서 공개하지 않은 패키지들은 해당 모듈 밖에서 절대로 접근 할 수 없다.
그러나 JDK 외에도 모듈 개념이 널리 받아들여질지 예측하기는 아직 이르다. 꼭 필요한 경우가 아니라면 당분간은 사용하지 않는 것이 좋다.