책에서 배운 내용대로만 접근제한자를 관리하면, 접근제한자는 권한을 적게 줄수록 좋으므로
package-private
을 사용하는 것이 무조건 옳아 보였다. 그러나 실제 현업에서는 거쳐가는 서비스를 관리한다는 특성 때문에 기피되고 있다는 걸 배울 수 있었다.어설프게 설계된 컴포넌트와 잘 설계된 컴포넌트의 가장 큰 차이는 바로 클래스 내부 데이터와 내부 구현 정보를 외부 컴포넌트로부터 얼마나 잘 숨겼느냐다. 잘 설계된 컴포넌트는 모든 내부 구현을 완벽히 숨겨, 구현과 API를 깔끔히 분리한다 [정보은닉, 캡슐화]
정보 은닉의 장점
- 시스템을 구성하는 컴포넌트들을 서로 독립시켜서 개발, 테스트, 최적화, 적용, 분석, 수정을 개별적으로 할 수 있게 해주는 것과 연관되어 있음
- 시스템 개발 속도를 높인다. 여러 컴포넌트를 병렬로 개발할 수 있기 때문 ← API로만 동작을 하기때문에 내부 구현이 개발중이더라도 다른 컴포넌트에서 해당 컴포넌트를 이용할 수가 있을것이기 때문
- 시스템 관리 비용을 낮춘다. 각 컴포넌트를 더 빨리 파악하여 디버깅할 수 있고, 다른 컴포넌트로 교체하는 부담도 적기 때문 : API로만 동작하기에 API 이용방법만 보면 됨
- 정보 은닉 자체가 성능을 높여 주지는 않지만, 성능 최적화에 도움을 줌. 완성된 시스템을 프로파일링하여 최적화할 컴포넌트를 정한 다음 다른 컴포넌트에 영향을 주지 않고 해당 컴포넌트만 최적화 할 수 있기 때문
- 소프트웨어 재사용성을 높임. 외부에 거의 의존하지 않고 독자적으로 동작할 수 있는 컴포넌트라면 그 컴포넌트와 함께 개발되지 않은 낯선 환경에서도 유용하게 쓰일 가능성이 크기 때문임
- 큰 시스템을 제작하는 난이도를 낮춰줌. 시스템 전체가 완성되지 않은 상태이더라도 개별 컴포넌트의 동작을 검증할 수 있기 때문
정보 은닉 기본 원칙
모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 한다.
- 패키지 외부에서 쓸 이유가 없다면 package-private으로 선언하기
- public으로 선언한다면 API가 되므로 주의하기
- 한 클래스에서만 사용 시, private static으로 중첩
- public일 필요가 없는 클래스의 접근 수준을 package-private 톱레벨 클래스로 좁히기
- 클래스의 공개 API를 세심히 설계한 후, 그 외의 모든 멤버는 private으로 만들자
- public 클래스의 protected는 접근 범위가 매우 넓기에 적을수록 좋음
- public 클래스의 필드는 되도록 public이 아니어야 함(예외 : 해당 클래스의 추상개념을 완성하는데 필요한 구성요소로서의 상수인 경우 public static final 가능)
- 톱레벨 클래스( 가장 바깥이라는 의미)나 인터페이스를 public으로 선언하면 공개 API가 되며, package-private으로 선언하면 해당 패키지 안에서만 이용가능
- 패키지 외부에서 쓸 이유가 없다면 package-private으로 선언하기 ⇒ API가 아닌 내부 구현이 되어 언제든 수정이 가능함. 클라이언트에 아무런 피해 없이 다음 릴리스에서 수정, 교체, 제거할 수 있다
- 반면 public으로 선언한다면 API가 되므로 하위 호한을 위해 영원히 관리해줘야만 한다! (끔찍)
- 한 클래스에서만 사용하는 package-private 톱레벨 클래스나 인터페이스는 이를 사용하는 클래스 안에 private static으로 중첩시켜보자 ⇒ 바깥 클래스 하나에서만 접근 가능함!
- 중요!! public일 필요가 없는 클래스의 접근 수준을 package-private 톱레벨 클래스로 좁히는 일
- 클래스의 공개 API를 세심히 설계한 후, 그 외의 모든 멤버는 private으로 만들자. 그런 다음 오직 같은 패키지의 다른 클래스가 접근해야 하는 멤버에 한하여(private 제한자를 제거해) package-private으로 풀어주자
- public 클래스에서 멤버의 접근 수준을 package-private에서 protected로 바꾸면 그 멤버에 접근할 수 있는 대상 범위가 엄청나게 넓어짐
- public 클래스의 protected 멤버는 공개 API이므로 영원히 지원돼야 함
- 또한 내부 동작 방식을 API 문서에 적어 사용자에게 공개해야 할 수도 있기에 protected 멤버의 수는 적을 수록 좋음
멤버 접근성을 좁히지 못하게 방해하는 제약 : 상위 클래스의 메서드를 재정의 할 때 그 접근 수준을 상위 클래스에서 보다 좁게 설정할 수 없음( LSP 원칙 때문에 )
- public 클래스의 필드는 되도록 public이 아니어야 함
- 필드가 가변 객체를 참조하거나 final이 아닌 인스턴스 필드를 public으로 선언하면 그 필드에 담을 수 있는 값을 제한할 힘을 잃게 됨 ↔ 그 필드는 불변식을 보장할 수 없음 & 필드 수정 시, 다른 작업(acquiring lock)을 할 수 없어서 Thread safe 하지 않음(동시성 보장x)
- 내부 구현을 바꾸고 싶어도 해당 필드는 외부에서 이미 참조를 하고 있기에 없애는 방식으로 리팩토링 불가
- [예외] 정적필드에서도 마찬가지이나, 해당 클래스가 표현하는 추상 개념을 완성하는 데 꼭 필요한 구성요소 로써의 상수라면 public static final 필드로 공개해도 좋음(반드시 기본 타입 or Immutable)
- 길이가 0이 아닌 배열은 모두 변경 가능하니 주의하기 → 클래스에서 public static final 배열 필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공해서는 안됨
private static final Thing[] PRIVATE_VALUES = { ... }; public static final List<Thing> VALUES = Collections.unmodifiableList( Arrays.asList(PRIVATE_VALUES)); private static final Thing[] PRIVATE_VALUES = { ... }; public static final Thing[] values() { return PRIVATE_VALUES.clone(); }
새로운 접근 수준 — 모듈 시스템 개념
- 패키지가 클래스들의 묶음이고, 모듈은 패키지들의 묶음임
- 모듈은 자신에 속하는 패키지 중 공개(export)할 것들을 (관례상 module-info.java 파일에) 선언함
- protected 혹은 public 멤버라도 해당 패키지를 공개하지 않았다면 모듈 외부에서는 접근할 수 없음
- 모듈 시스템을 활용하면 클래스를 외부에 공개하지 않으면서도 같은 모듈을 이루는 패키지 사이에서는 자유롭게 공유할 수 있음
- 모듈의 장점을 제대로 누리려면 해야 할 일이 많음.. 꼭 필요한 경우 아니라면 당분간은 사용하지 않는 게 좋을 것같다고 함