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

Loopy·2022년 6월 25일
0

이펙티브 자바

목록 보기
15/76
post-thumbnail

잘 설계된 컴포넌트는, 모든 내부 구현화 데이터 정보를 외부로부터 완벽히 숨겨, 구현과 API를 깔끔하게 분리한다. 이렇게 오직 API를 통해서만 다른 컴포넌트와 소통하며 서로의 내부 동작 방식에 개의치 않는 이 개념을, 바로 정보 은닉(캡슐화) 라고 한다.

정보 은닉의 장점에는 어떤 것들이 있을까?

  1. 여러 컴포넌트를 병렬로 개발 가능하기 때문에, 시스템 개발 속도를 높인다.

  2. 시스템 관리 비용을 낮춘다. 각 컴포넌트를 더 빨리 파악하여 디버깅할 수 있고, 다른 컴포넌트를 교체하는 부담도 적기 때문이다.

  3. 정보 은닉 자체가 성능을 높여주지는 않지만, 성능 최적화에 도움을 준다.(아이템 67)

  4. 외부에 의존하지 않고 독자적으로 동작할 수 있는 컴포넌트이기 때문에, 소프트웨어 재사용성을 높인다.

  5. 큰 시스템을 제작하는 난이도를 낮춰준다. 시스템 전체가 아직 완성되지 않은 상태에서도 개별 컴포넌트의 동작을 검증할 수 있기 때문이다.

자바는 정보 은닉을 위한 다양한 방법들을 제공한다.

☁️ 접근 제어 메커니즘

요소의 접근성은 그 요소가 선언된 위치와 접근 제한자(private, protected, public)로 정해진다. 기본 원칙은, 모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 한다는 것이다. (가장 낮은 접근 수준을 부여해야함)

1. 톱 레벨 클래스와 인터페이스 접근자

톱레벨 클래스와 인터페이스에 부여할 수 있는 접근 수준은 package-privatepublic 두 가지이다.

  1. public : 공개 API가 되며, 하위 호환을 위해 관리가 필요하다.
  2. package-private : 해당 패키지 안에서만 이용할 수 있으며, 클라이언트에 피해 없이 다음 릴리스에서 수정,교체,제거가 가능하다.

만약 한 클래스에서만 사용하는 package-private 톱 레벨 클래스나 인터페이스는, 사용하는 클래스 안에 private static으로 중첩시키면 바깥 클래스 하나에서만 접근 할 수 있게 된다.(아이템 24)

2. 멤버 접근자

멤버(필드, 메서드, 중첩 클래스, 중첩 인터페이스)에 부여할 수 있는 접근 수준은 네가지이다.

  1. private : 멤버를 선언한 톱레벨 클래스에서만 접근 가능
  2. package-private : 멤버가 소속된 패키지 안의 모든 클래스에서 접근 가능. 접근 제한자를 명시하지 않았을때 기본 접근 수준
  3. protected : pakage-private의 접근 범위를 포함하며, 이 멤버를 선언한 클래스의 하위 클래스에도 접근 가능
  4. public : 모든 곳에서 접근 가능

공개 API 제외 모든 멤버는 private으로 만든 후, 오직 같은 패키지의 다른 클래스가 접근해야 하는 멤버에 한하여 package-private으로 풀어주자.

package-privateprivate 멤버는 클래스의 구현에 해당하므로 공개 API에 영향을 주지 않지만, Serializable 을 구현한 클래스에서는 필드들로 의도치 않게 공개 API가 될 수 있으니 주의하자. Serializable 역직렬화 과정에서 private 필드가 노출되기 때문이다.

멤버 접근성 좁히기 방해 제약

단 상위 클래스의 메서드에서 재정의할때는, 리스코프 치환 원칙으로 인해 그 접근 수준을 상위 클래스에서보다 좁게 설정할 수 없다.

🔖 리스코프 치환 원칙
상위 클래스의 인스턴스는 하위 클래스의 인스턴스로 대체해 사용할 수 있어야 한다. 쉽게 말해서, 부모 클래스의 행동 규약을 자식 클래스가 위반하면 안된다.(다형성)

☁️ 주의사항

주의할 점으로, public 클래스의 인스턴스 필드는 되도록 public 이 아니어야 한다.

필드가 가변 객체를 참조하거나, final이 아닌 인스턴스 필드를 공개하면 불변식을 보장할 수 없게 되기 때문이다. 또한 필드가 수정될 때 다른 작업 수행이 불가능하므로 public 가변 필드를 갖는 클래스는 스레드 안전하지 않다.

단, 상수라면 public static final 필드로 공개해도 괜찮으며, 해당 필드는 반드시 기본 타입 값이나 불변 객체를 참조해야 한다. 만약 배열이라면, 아래와 같이 변경하자.

public static final Thing[] VALEUS = {...};
  1. private 으로 변경한 후 public 불변 리스트를 추가
private static final Thing[] VALEUS = {...};
public static final List<Thing> VALUES = 
	Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
  1. 배열을 private 로 만들고 그 복사본을 반환하는 public 메서드를 추
private static final Thing[] VALEUS = {...};
public static final Thing[] values() {
	return PRIVATE_VALUES.clone();
 }

☁️ 모듈 시스템에서의 접근 제한자

자바 9부터는 모듈 시스템이라는 개념이 도입되면서, 두 가지 암묵적 접근 수준이 추가되었다.

🔖 모듈(Module)
패키지들의 묶음을 말하며 자신에 속하는 패키지 중 공개(export)할 것들을 module-info.java 파일에 선언한다. 모듈 시스템을 활용하면 클래스를 외부에 공개하지 않으면서도 같은 모듈을 이루는 패키지 사이에서는 자유롭게 공유가 가능하다는 장점이 있다.

모듈에 적용되는 암묵적 접근 수준은 public, protected 수준과 같으나 그 효과가 모듈 내부로 한정된다는 점만 다르다. 이 접근 수준을 활용한 예로 JDK가 있으며, 자바 라이브러리에서 공개하지 않은 패키지들은 해당 모듈 밖에서 절대 접근 할 수 없다.

profile
개인용으로 공부하는 공간입니다. 잘못된 부분은 피드백 부탁드립니다!

0개의 댓글