본문 바로가기

Dev Book Review/Effective Java

[Effective Java] item 15. 클래스와 멤버의 접근 권한을 최소화하라

잘 설계된 컴포넌트 : 클래스 내부 데이터와 내부 구현정보를 외부 컴포넌트로부터 얼마나 잘 숨겼는가
오직 API를 통해서만 다른 컴포넌트와 소통한다. [정보은닉, 캡슐화]

 

1. 정보 은닉의 장점

  1. 시스템 개발 속도를 높인다 - 여러 컴포넌트 병렬 개발
  2. 시스템 관리 비용을 낮춘다 - 컴포넌트 교체 비용 하락
  3. 성능 최적화에 도움이 된다 - 완성된 시스템을 프로파일리하고 최적화할 컴포넌트만 가능하다.
    [아이템 67 - 최적화는 신중히 하라]
  4. 소프트웨어 재사용성을 높인다 - 의존성이 낮은 컴포넌트라면 다른 환경에서도 유용
  5. 큰 시스템을 제작하는 난이도를 낮춘다 - 개별 컴포넌트 동작 검증

 

2. 정보 은닉 기본원칙

모든 클래스와 멤버의 접근성을 가능한 한 좁힌다 (최대한 public을 지양하는 듯)
접근성 : 요소가 선언된 위치 + 접근 제한자 (private, protected, public)

 

ㄱ. 톱레벨 클래스, 인터페이스

public : 공개 api / package-private : 패키지 안에서만 사용

package-private : 클라이언트에 피해 없이 다음 릴리즈에서 수정,교체,제거 가능

 

ㄴ. private static 중첩 클래스

한 클래스에서만 사용하는 package-private 클래스의 경우에는 private staticd으로 중첩시키자
[아이템 24 - 멤버클래스는 되도록 static으로 만들어라]

 

ㄷ. 멤버필드

private / package-private / protected / public

  1. private 으로 만들기
  2. package-private으로 멤버 접근 수준 풀기
  3. 권한 풀기가 잦다 : 컴포넌트 분해 여부를 좀 더 고민한다.
    Serializabl을 구현한 클래스에서는 의도치 않게 (package-private, private 멤버가) 공개 API가 될 수 있으니 조심
  4. protected로 멤버 접근 수준 풀기

Public 클래스의 protected 멤버는 공개 API이므로 주의
[아이템 19 - 상속을 고려해 설계하고 문서화하라. 그러지 않았다먼 상속을 금지하라]

 

3. 멤버 접근성 제약

상위클래스의 메서드를 재 정의할 때 상위보다 좁게 설정을 할 수 없다.

[아이템10 - equals는 일반 규약을 지켜 재정의하라] : 리스코프 치환원칙 (상위클래스 인스턴스는, 하위클래스 인스턴스로 대체가 가능해야한다.)

 

4. 주의점

코드 테스트만을 위해 클래스, 인터페이스, 멤버를 공개 API로 만들면 안된다.

 

ㄱ. public 클래스의 인스턴스 필드는 되도록 public이 아니어야한다.

[아이템 16 - public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라]

필드와 관련된 모든 것의 불변성을 잃기 때문이다.

public 가변 필드를 갖는 클래스는 스레드 안전하지 않다.

심지어 필드가 final이면서 불변 객체를 참조하더라도 문제는 여전히 남는다. 내부 구현을 바꾸고 싶어도 그 public 필드를 없애는 방식으로는 리팩터링 할 수 없다

헷갈렸지만 : 어찌됐든 public필드일 경우는 필드를 없애는 방식의 리팩터링이 안되서 쓰지 말라는 내용으로 이해함

정적필드도 마찬가지이다.

 

ㄴ. 정적필드에서의 public 필드 예외

public static final 필드 공개는 허용한다. 이런 필드는 반드시 기본 타입 값이나 불변 객체를 참조해야 한다.
[아이템 68 - 일반적으로 통용되는 명명 규칙을 따르라]
[아이템 17 - 변경 가능성을 최소화하라]

 

ㄷ. 클래스에서 public static final 배열필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공해서는 안된다.

클라이언트에서 배열의 내용을 수정할 수 있기 때문이다.

[권장 방법]

1. public 불변리스트를 추가한다.

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

2. 복사본을 반환하는 public 메서드를 추가한다 : 방어적 복사

private static final Thing[] PRIVATE_VALUES = {...};
public static final Thing[] values(){
  return PRIVATE_VALUES.clone();
}

타입, 성능에 따라서 사용하자.

 

5. Java 9 : 모듈 시스템 개념의 도입

패키지 : 클래스의 묶음 // 모듈 : 패키지의 묶음

모듈 : 자신에 속하는 패키지 중 공개 (export) 할 것들을 선언한다. [module-info.java 파일에]

클래스를 외부에 공개하지 않으면서도 같은 모듈을 이루는 패키지 사이에서 자유롭게 공유할 수 있다.

암묵적 접근 수준 : public, protected 수준의 효과가 모듈 내부로 한정된다.

 

ㄱ. 모듈에 적용되는 새로운 두 접근 수준은 주의해야한다.

모듈의 jar파일을 모듈 경로가 아니라 애플리케이션 클래스 패스에 두면, 모듈안 패키지가 모듈이 없는 것 처럼 행동한다.

일반 클래스 파일이 있는 것처럼 행동한다 : 모듈 시스템 암묵적 접근 수준이 해제된다

JDK : 자바라이브러리에서 공개하지 않은 패키지는 모듈 밖에서 접근 할 수 없다.

 

ㄴ. 모듈의 장점을 누리기 위한 조치

  • 패키지를 모듈 단위로 묶는다.
  • 모듈 선언에 패키지들의 의존성을 명시한다.
  • 소스트리 재배치
  • 모듈 안으로 부터 (모듈 시스템을 적용하지 않는) 일반 패키지로의 모든 접근에 특별한 조치를 취해야한다.

그러나 모듈의 개념은 아직은 사용하지 않는게 좋은 것 같다.