-
[이펙티브자바]클래스와 멤버의 접근 권한을 최소화하라_아이템15이펙티브 자바 2024. 5. 31. 20:58
잘 설계된 컴포넌트는 내부 구현을 완벽히 숨겨, 구현과 API를 분리한다.
[정보은닉의 장점]
1. 시스템의 개발 속도를 높인다. => 여러 컴포넌트 병렬 개발 가능하다.
2. 시스템 관리 비용 낮춘다. => 코드 파악 용이하여 디버깅 가능하고 컴포넌트 교체 용이하다.
3. 성능 최적화에 도움을 준다. => 다른 컴포넌트에 영향을 주지 않고 해당 컴포넌트만 최적화 가능하다.
4. 소프트웨어 재사용을 높임. => 외부 의존 없이 동작가능한 컴포넌트라면 낯선 환경에서도 유용하게 사용될 가능성 크다.
5. 큰 시스템을 제작하는 난이도 낮춰줌. => 시스템 전체 완성 전, 개발 컴포넌트의 동작 검증 가능하다.
[기본 원칙]
모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 한다. => 외부에서도 사용하려면 public, 외부에서 사용하지 않는다면 package-private.
private: 멤버를 선언한 톱레벨 클래스만 접근가능.
package-private: 멤버거 소속된 패키지 안의 모든 클래스에서 접근할 수 있다. 접근 제한자를 명시하지 않았을 때 적용되는 패키지 접근 수준이다.(인터페이스의 멤버는 기본적으로 public 적용)
protected: package-private의 접근 범위를 포함하며, 이 멤버를 선언한 클래스의 하위 클래스에서도 접근할 수 있다.
public: 모든 곳에서 접근할 수 있다.
private과 package-private멤버는 모두 해당 클래스의 구현에 해당하므로 보통은 공개 API에 영향을 주지 않는다. => Serializable을 구현한 클래스에서는 그 필드들도 의도치 않게 공개 API가 될 수도 있다.
protected 멤버의 수는 적을수록 좋다. 공개API이므로 내부 동작 방식을 API문서에 적어 사용자에게 공개해야 할 수도 있기 때문이다.
상위 클래스의 메서드를 재정의할 때 그보다 좁게 설정할 수 없다(리스코프 치환원칙) => 이 규칙을 어기면 하위 클래스를 컴파일할 때 컴파일 오류가 난다.
클래스의 private멤버를 package-private까지 풀어주는 것은 허용항 수 있지만 그 이상은 안된다. => **테스트만을 위해 클래스, 인터페이스, 멤버를 공개API로 만들면 안 된다.
**public 클래스의 인스턴스 필드는 되도록 public이 아니어야 한다.
필드가 가변 객체를 참조하거나, final이 아닌 인스턴스 필드를 public으로 선언하면 그 필드에 담을 수 있는 값을 제한할 힘을 잃게 된다. => 불변식을 보장X
+필드가 수정될 때 다른 작업을 할 수 없게 되므로 public 가변 필드를 갖는 클래스는 일반적으로 스레드가 안전하지않다.
+내부 구현을 바꾸고 싶어도 public필드를 없애는 방식으로는 리팩토링할 수 넚다.
**public static final 필드는 공개해도 좋다.
상수의 이름은 대문자 알파벳 + 각 단어 사이에 _을 넣는다. 반드시 기본 타입 값인나 불변 객체를 참조해야 한다. => 가변 객체를 참조한다면 final이 아닌 필드에 적용되는 모든 불이익이 적용됨. 참조된 객체 자체는 수정되어 끔찍한 결과를 초래할 수 있다.
길이0이 아닌 배열은 모두 변경 가능하다.(주의사항)
=>클래스에서 public static final 배열 필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공해서는 안된다.
[예제1]//보안 허점이 숨어 있다. public static final Thing[] VALUES = {...};
어떤 IDE가 생성하는 접근자는 private 배열 필드의 참조를 반환하여 이 같은 문제를 일으킴.
첫 번째 방법, 예제1의 public배열을 private으로 만들고 public불변 리스트를 추가하는 것이다.
[예제2]private static final Thing[] PRIVATE_VALUES = {...}; public static final List<Thing> VALUES = Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
두 번째는 배열을 private으로 만들고 그 복사본을 반환하는 public메서드를 추가하는 방법(방어적 복사)
[예제3]private static final Thing[] PRIVATE_VALUES = {...}; public static final Thing[] values() { return PRIVATE_VALUES.clone(); }
예제2,3에서 어떤 반환타입이 쓰기 편할지, 성능이 나올지 고민해보자.
자바9에서는 모듈 시스템이 도입되서 암묵적 접근 수준이 추가되었다. protected 혹은 public 멤버라도 해당 패키지를 공개하지 않았다면 모듈 외부에서는 접근할 수 없다. 모듈 시스템을 활용하면 클래스를 외부에 공개하지 않으면서 같은 모듈을 이루는 패키지 사이에서는 자유롭게 공유할 수 있다.
암묵적 접근 수준은 바로 이 숨겨진 패키지 안에 있는 public 클래스의 public클래스의 public 또는 protected멤버와 관련있다. 모듈 내부로 한정되는 변종이다. 상황이 벌어지더라도 패키지들 사이에서 클래스들을 재배치하면 대부분 해결된다.
모듈의 JAR파일을 자신의 모듈 경로가 아닌 애플리케이션의 클래스패스(classpath)에 두면 그 모듈 안의 모든 패키지는 모듈이 없는 것처럼 밖에서 접근할 수 있다. 예로는 JDK이다.
자바 라이브러리에 공개하지 않은 패키지들은 해당 모듈 밖에서는 절대로 접근할 수 없다.
[모듈의 장점 활용]
1. 패키지들을 모듈 단위로 묶고 모듈 선언에 패키지들의 모든 의존성을 명시한다.
2. 소스트리 재배치 후 모듈 안으로부터 일반 패키지로의 모든 접근에 특별한 조치를 취해야 한다.
*아직은 예측이 불가능하기에 당분간 사용하지 않는게 좋다.'이펙티브 자바' 카테고리의 다른 글
[이펙티브자바]변경 가능성을 최소화하라_아이템17 (0) 2024.06.26 [이펙티브자바]public클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라_아이템16 (0) 2024.06.05 [이펙티브자바]Comparable을 구현할지 고려하라_아이템14 (0) 2024.01.18 [이펙티브자바]clone 재정의는 주의해서 진행하라_아이템13 (0) 2024.01.15 [이펙티브자바]toString을 항상 재정의하라_아이템12 (0) 2024.01.10