본문 바로가기

Effective Java

[Effective Java] 아이템15 - 클래스와 멤버의 접근 권한을 최소화하라

정보 은닉

: 잘 설계된 컴포넌트는 모든 내부 구현을 완벽히 숨겨, 구현과 API를 깔끔히 분리한 것

 

정보 은닉의 장점

  • 여러 컴포넌트를 병렬로 개발해 시스템 개발 속도를 높임
  • 시스템 관리 비용을 낮춤 (각 컴포넌트를 더 빨리 파악하여 디버깅할 수 있고, 다른 컴포넌트로 교체하는 부담도 적기 때문)
  • 정보 은닉 자체가 성능을 높여주지는 않지만, 성능 최적화에 도움을 줌
  • 소프트웨어 재사용성을 높임 (외부에 거의 의존하지 않고 독자적으로 동작할 수 있는 컴포넌트라면, 그 컴포넌트와 함께 개발되지 않은 낯선 환경에서도 유용하게 쓰일 가능성이 크기 때문)
  • 큰 시스템을 제작하는 난이도를 낮춰줌 (시스템 전체가 아직 완성되지 않은 상태에서도 개별 컴포넌트의 동작을 검증할 수 있기 때문)

클래스, 인터페이스, 멤버의 접근성(접근 허용 범위)은 그 요소가 선언된 위치와 접근 제어자(private, protected, public)로 정해짐

 

모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 한다!

 

접근 제어자

  • private: 멤버를 선언한 톱레벨 클래스에서만 접근 가능
  • package-private: 멤버가 소속된 패키지 안의 모든 클래스에서 접근 가능 (deflaut 접근 제어자 - 인터페이스의 멤버는 기본적으로 public 적용)
  • protected: package-private의 접근 범위를 포함하며, 이 멤버를 선언한 클래스의 하위 클래스에서도 접근 가능
  • public: 모든 곳에서 접근 가능
  같은 클래스 같은 패키지 같은 패키지의
상속 클래스
다른 패키지의
상속 클래스
전체 클래스
public O O O O O
protected O O O O  
package-private O O O    
private O        

 

1. 클래스의 공개 API를 설계한 후, 그 외의 모든 멤버는 private으로 생성

2. 같은 패키지의 다른 클래스가 접근해야 하는 멤버에 한하여 package-private으로 변경

→ 권한을 풀어주는 일을 자주 하게 된다면 시스템에서 컴포넌트를 더 분해해야 하는 것은 아닌지 다시 고민할 필요有

 

✖️ public 클래스에서는 멤버 접근 수준을 package-private에서 protected로 바꾸는 순간 그 멤버에 접근할 수 있는 대상 범위가 넓어지므로, protected 멤버의 수는 적을수록 좋음

 

 

위처럼 먼저 접근 범위를 좁힌 후 서서히 풀어나아가야 하지만,

접근 수준을 상위 클래스에서보다 좁게 설정할 수 없기 때문에 LIS를 주의해 클래스를 설계해야 함!

LIS(리스코프 치환 원칙): 상위 클래스의 인스턴스는 하위 클래스의 인스턴스로 대체해 사용할 수 있어야 한다는 규칙

 

public 클래스의 인스턴스 필드는 되도록 public이 아니어야 함

  • 필드와 관련된 모든 것은 불변식을 보장할 수 없게 됨
  • public 가변 필드를 갖는 클래스는 일반적으로 스레드 안전하지 않음
  • 내부 구현 변경을 원할 때 public 필드를 없애는 방식으로 리팩토링할 수 없음

예외: 해당 클래스가 표현하는 추상 개념을 완성하는 데 꼭 필요한 구성요소로써의 상수 (불변 객체를 참조하는 public static final 필드)

 

길이가 0이 아닌 배열은 모두 변경 가능하니 주의해야 함

클래스에서 public static final 배열 필드를 두거나 이 필드를 반환하는 메서드를 제공해서는 안 됨

 

// 보안 허점이 숨어 있다.
public static final Thing[] VALUES = {...};

 

 

첫 번째 방법

: private 배열로 변경 + public 불변 리스트 추가

 

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

 

 

두 번째 방법

: private 배열로 변경 + 복사본 반환하는 public 메서드 추가

 

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

 

모듈 시스템의 두 가지 암묵적 접근 수준

: 각각 public 수준과 protected 수준과 같으나, 그 효과가 모듈 내부로 한정되는 변종인 것

  • 모듈은 자신에 속하는 패키지 중 공개(export)할 것들을 선언
  • protected 혹은 public 멤버라도 해당 패키지를 공개하지 않았다면 모듈 외부에서 접근 불가

 

 

프로그램 요소의 접근성은 가능한 한 최소한으로 하자. 꼭 필요한 것만 골라 최소한의 public API를 설계하자. 
public 클래스는 상수용 public static final 필드 외에는 어떠한 public 필드도 가져서는 안된다.