본문은 Effective Java를 읽고 간단하게 정리한 글입니다. 필요에 따라 생략/수정된 부분이 있을 수 있으며, 내용이 추후 변경될 수 있습니다.
선결론
- 프로그램 요소의 접근성은 가능한 한 최소한으로 유지하자
- 꼭 필요한 것만 골라 최소한의 public API를 설계하자
- 이 모든 것은 '정보 은닉의 장점'을 취하기 위해서..
- public 클래스는 상수용 public static final 필드 외에는 어떠한 public 필드도 가져서는 안 된다
- public static final 필드가 참조하는 객체가 불변인지 확인하자
정보 은닉의 장점
- 여러 컴포넌트를 병렬로 개발 가능 → 시스템 개발 속도 향상
- 컴포넌트가 작게 나눠져 있기 때문에 디버깅도 쉽고 교체도 쉬움 → 시스템 관리 비용 낮춤
- 컴포넌트간 결합도가 낮기 때문에 특정 컴포넌트를 최적화하기 쉬움 → 성능 최적화에 도움
- 외부 의존성이 적은 컴포넌트 → 소프트웨어 재사용성 향상
- 단위 테스트가 쉬워짐 → 큰 시스템을 제작하는 난이도 하락
정보 은닉에 대한 자세한 설명은 여기에서
정보 은닉을 준수하는 컴포넌트 설계
기본 원칙
- 모든 클래스와 멤버의 접근성을 가능한 한 좁힌다
- 패키지 내부에서 사용되는 클래스는 package-private(default)로 선언하자
- 자연스럽게 개방-폐쇄 원칙을 준수하게 됨
클래스 접근성 좁히기
- 한 클래스에서만 사용되는 package-private 톱레벨 클래스나 인터페이스는 이를 사용하는 클래스 안에 private static으로 중첩시킨다
- static으로 inner 클래스를 만들어 사용하라는 의미
- 외부 패키지에서 사용하지 않는 public 클래스는 package-private으로 접근 수준을 축소시킨다
- public 클래스는 해당 패키지의 API인 반면, package-private 톱레벨 클래스는 내부 구현에 속하기 때문이다
멤버 접근성 좁히기
- 멤버는 기본적으로 private으로 선언하되, 다른 패키지에서 접근해야 한다면 package-private으로 풀어준다
- 직렬화 과정에서 private 필드도 바이트로 변환되기 때문에
- 제외하고 싶다면 transient 키워드 추가
- public 클래스에서 protected 멤버의 수는 최대한 적게 만들어야 한다
- public 클래스의 protected 멤버는 공개 API이므로 외부에 노출됨, 이에 따라 지원이나 관리가 요구됨
- 접근성을 좁히지 못하게 만드는 제약이 존재한다
- 상위 클래스의 메서드를 오버라이딩할 땐 접근 수준을 상위 클래스보다 좁게 설정할 수 없다
- 테스트만을 위해 클래스, 인터페이스, 멤버를 공개 API로 만들면 안 된다
- private 클래스를 public 클래스로 변경하는 경우
- public 클래스의 private/package-private 멤버를 protected/public으로 바꾸는 경우
- public 클래스의 인스턴스 변수는 되도록 public이 아니여야 한다
- 해당 필드와 관련된 모든 것은 불변식을 보장할 수 없게 된다는 뜻
- 불변식(invariant)이란?
- 불변식은 메서드가 실행되기 전과 후에 반드시 만족시켜야 하는 조건이다(오브젝트)
- 필드의 값이 임의로 바뀔 수 있음
- 즉, 어느 메서드에서 이 필드의 값을 사용할 때 호출 시점에 따라 결과가 달라짐
- 불변식(invariant)이란?
- 일반적으로 스레드 안전하지 않다 → 필드를 수정할 때 락 획득과 같은 별도의 작업을 수행하지 못하므로
- 추상 개념을 완성하는 데 꼭 필요한 구성요소로써의 상수(?)라면 public static final 필드로 공개해도 좋다
- 말이 되게 어려운데.. 하위 클래스에서도 공통적으로 사용하는 상수라는 의미..
- 해당 필드와 관련된 모든 것은 불변식을 보장할 수 없게 된다는 뜻
- public static final 배열 필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공하면 안 된다
- 대신 다음과 같은 방법을 사용하자 → public을 private으로 바꾸고 불변리스트나 복사본 배열 반환
class Thing {
int id;
public Thing(int id) {
this.id = id;
}
}
class NotSafeClass {
// 클라이언트에서 해당 필드에 참조하여 원본 배열을 변경가능
public static final Thing[] PRIVATE_VALUES = { new Thing(1), new Thing(2), new Thing(3)};
}
class SafeClass1 {
private static final Thing[] PRIVATE_VALUES = { new Thing(1), new Thing(2), new Thing(3)};
// public 불변 리스트 반환, 원본 배열에 접근 불가능
public static final List<Thing> VALUES = Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
}
class SafeClass2 {
private static final Thing[] PRIVATE_VALUES = { new Thing(1), new Thing(2), new Thing(3)};
// public 복사본 배열 반환(방어적 복사), 원본 배열에 접근 불가능
public static final Thing[] values() {
return PRIVATE_VALUES.clone();
}
}
모듈을 통한 접근 수준
- 자바 9에서 추가된 모듈 시스템을 통해 두 가지 암묵적 접근 수준(?)이 추가되었다는데..
- module의 공개(export)여부에 따라 접근이 되냐/안되냐 여부가 갈린다는 말로 이해함
- 앞서 얘기했던 접근제어자는 (private/package-private/protected/public ) 네 가지 수준
- 여기는 export/non-export 두 가지 수준
- module의 공개(export)여부에 따라 접근이 되냐/안되냐 여부가 갈린다는 말로 이해함
- 모듈은 패키지들의 묶음
- 자신에 속하는 패키지 중 공개할 것들을 (관례상 module-info.java 파일에) 선언
- protected/public 멤버라도 해당 패키지를 공개하지 않으면 외부에서 접근 불가능
- 모듈 안에서는 상관없이 접근 가능
- 모듈의 jar 파일 경로에 따라 의도치 않은 접근이 일어날 수 있음
- A Guide To Java 9 Modularity
'책 > Effective Java' 카테고리의 다른 글
[이펙티브 자바] 아이템 17: 변경 가능성을 최소화하라 (0) | 2022.05.03 |
---|---|
[이펙티브 자바] 아이템 16: public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라 (0) | 2022.05.02 |
[이펙티브 자바] 아이템14: Comparable을 구현할지 고려하라 (0) | 2022.04.27 |
[이펙티브 자바] 아이템13: clone 재정의는 주의해서 진행하라 (0) | 2022.04.27 |
[이펙티브 자바] 아이템12: ToString을 항상 재정의하라 (0) | 2022.04.26 |