15. 클래스와 멤버의 접근 권한을 최소화하라

신명철·2022년 2월 15일
0

Effective Java

목록 보기
13/80

잘 설계된 컴포넌트란?

  • 캡슐화가 얼마나 잘되었는지
  • 노출되는 API와 실제 구현이 얼마나 잘 분리되었는지
  • 메시지를 주고받는 두 컴포넌트가 서로의 내부 동작을 신경쓰지 않는지

정보 은닉(캡슐화)의 장점

  1. 시스템 개발 속도를 높인다. 여러 컴포넌트를 병렬로 개발할 수 있기 때문이다.
  2. 시스템 관리 비용을 낮춘다. 각 컴포넌트를 더 빨리 파악해 디버깅할 수 있고, 다른 컴포넌트로 대체하는 것도 부담이 적기 떄문이다.
  3. 성능 자체를 높여주지는 않지만, 성능 최적화에 도움을 준다. 완성 시스템을 프로파일링해서 최적화할 컴포넌트를 정하고 다른 컴포넌트에 영향을 주지 않고 해당 컴포넌트만 최적화할 수 있기 때문이다.
  4. SW 재사용성을 높인다. 외부에 의존하지 않고 독자적으로 동작할 수 있는 컴포넌트라면 그 컴포넌트와 함께 개발되지 않은 낯선 환경에서도 유용하게 쓰일 가능성이 크기 때문이다.
  5. 큰 시스템을 제작하는 난이도를 낮춰준다. 시스템 전체가 아직 완성되지 않은 상태에서도 개별 컴포넌트의 동작을 검증할 수 있기 때문이다.

정보 은닉의 핵심

정보 은닉의 핵심은 접근제어자이다.

기본 원칙 : 모든 클래스와 멤버의 접근성을 가능한 좁혀야한다.

  • private : 멤버를 선언한 톱레벨 클래스에서만 접근 가능
  • package-private : 멤버가 소속된 패키지 안의 모든 클래스에서 접근 가능
  • protected : package-private + 하위 클래스에서 접근 가능
  • public : 모든 곳에서 접근 가능

그런데, 리스코프 원칙에 부합하기 위해서 하위 클래스는 상위 클래스보다 좁혀질 수 없다는 제약이 있다. 이는 접근성을 좁히지 못하게 방해하는 제약이 된다.

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

필드가 가변 객체를 참조하거나 final 이 아닌 인스턴스 필드를 public 으로 선언하면 그 필드에 담을 수 있는 값을 제한할 수 없게 된다.

그리고 필드를 수정할 때 다른 작업을 하지 못하기 때문에 스레드 안전하지 못하다. 필드가 final 이면서 불변 객체를 참조하더라도 public 필드를 없애는 방식으로 리팩터링하지 못한다는 문제가 있다.

예외가 있는데, 해당 클래스가 표현하는 추상 개념을 완성하는데 꼭 필요한 구성요소의 상수라면 public static final 필드를 공개해도 좋다.

이런 필드는 반드시 기본 타입이나 불변 객체를 참조해야 한다. 다른 객체를 참조하지 못하지만 참조된 객체는 수정될 수 있기 때문이다.

길이가 0 이 아닌 배열

길이가 0 이 아닌 배열은 모두 변경 가능하기 때문에 조심해야 한다. 클래스에서 public static final 배열 필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공하면 안된다. 이 배열의 내용을 수정할 수 있게 되기 때문이다.

이에 대한 해결책은 두가지다.

private static fianl Thing[] PRIVATE_VALUES = { ... };
public static final List<Thing> VALUES = Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
  • 첫번째 해결책은 public 배열을 private 으로 만들고 public 불변 리스트를 추가하는 것이다.
private static fianl Thing[] PRIVATE_VALUES = { ... };
public static final Thing[] values() {
	return PRIVATE_VALUES.clone();
}
  • 두번째 해결책은 배열을 private 으로 만들고 그 복사본을 반환하는 public 메서드를 제공하는 방법이다.
profile
내 머릿속 지우개

0개의 댓글