Posts [이펙티브자바] 아이템15-클래스와 멤버의 접근 권한을 최소화하라
Post
Cancel

[이펙티브자바] 아이템15-클래스와 멤버의 접근 권한을 최소화하라

컴포넌트 설계

  • 어설프게 설계된 컴포넌트와 잘 설계된 컴포넌트의 가장 큰 차이는 바로 클래스의 내부 데이터와 구현 정보를 외부 컴포넌트로부터 얼마나 잘 숨겼느냐이다.
  • 잘 설계된 컴포넌트는 모든 내부 구현을 완벽히 숨겨, 구현과 API를 깔끔히 분리한다. 오직 API를 통해서만 다른 컴포넌트와 소통하며 서로의 내부 동작 방식에는 전혀 개의치 않는다.
  • 정보 은닉 혹은 캡슐화라고 하는 이 개념은 SW 설계의 근간이 되는 원리이다.

정보 은닉의 장점

1) 개발 속도를 높여준다. 여러 컴포넌트를 병렬로 개발할 수 있기 때문이다. 2) 시스템 관리 비용을 낮춘다. 각 컴포넌트를 더 빨리 파악하여 디버깅할 수 있고 다른 컴포넌트로 교체하는 부담도 적기 때문이다. 3) 최적화할 컴포넌트를 발견하기에 용이하므로 성능 최적화에 도움을 준다. 4) 재사용성을 높인다. 5) 큰 시스템을 제작하는 난이도를 낮춘다.

접근 제한자

  • 접근 제한자를 제대로 활용하는 것이 정보 은닉의 핵심이다.
  • 기본 원칙은 모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 하는 것이다.
  • 만약 패키지 외부에서 쓸 이유가 없다면 package-private으로 선언하자.
    • 그러면 이들은 API가 아닌 내부 구현이 되어 언제든 수정할 수 있다. 즉, 클라이언트에 아무런 피해 없이 다음 릴리스에서 수정, 교체, 제거할 수 있다.
    • 반면 public으로 선언한다면 API 가 되므로 영원히 관리해줘야 한다.
  • public일 필요가 ㅇ벗는 클래스의 접근 수준을 package-private 톱레벨 클래스로 좁히는 일이다.
    • public 클래스는 그 패키지의 API인 반면, package-private 톱 레벨 클래스는 내부 구현에 속하기 때문이다.

접근 제한자의 종류

  • private: 멤버를 선언한 톱레벨 클래스에서만 접근 가능하며 그 외에는 접근이 불가하다.
  • package-private: 소속된 패키지안의 모든 클래스에서 접근 가능하며 그 외에는 접근이 불가하다.
    • 접근제한자를 명시하지 않았을때 적용되는 패키지 접근 수준이며 인터페이스의 멤버는 기본적으로 public이 적용된다.
  • protected: package-private의 접근 범위를 포함하며, 이 멤버를 선언한 클래스의 하위 클래스에서도 접근 가능하다.
  • public: 모든 곳에서 접근할 수 있다.

클래스의 공개 API를 세심히 설계 후, 그외의 모든 멤버는 private으로 만들자.

  • 오직 같은 패키지안의 다른 클래스가 접근해야 하는 멤버에 한하여 (private 제한자를 제거해) package-private 으로 풀어주자.
    • 개인적으로 같은 패키지의 다른 클래스의 필드에 직접 접근하는게 정말 좋은 것일까? 데이터를 변경하는 포인트들을 관리하기 복잡해지지 않을까? 하는 생각이 든다.

private과 package-private 멤버는 모두 해당 클래스의 구현에 해당하므로 보통은 공개 API에 영향을 주지 않는다.

  • 단, Serializable을 구현한 클래스에선 그 필드들도 의도치 않게 공개 API가 될 수 도 있다.(아이템 86, 87)

public 클래스에선 package-private에서 protected로 바꾸는 순간 그 멤버에 접근할 수 있는 대상 범위가 엄청나게 넓어진다.

  • protected 멤버는 공개 API이므로 영원히 지원돼야 한다.
  • 또한 내부 동작 방식을 API문서에 적어 사용자에게 공개해야 할 수도 있다.(아이템19)
  • 따라서 protected 멤버의 수는 적을수록 좋다.

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

  • 개인적인 생각으로 테스트 코드도 정말 중요한 필수불가결한 요소지만 애플리케이션 코드에 영향을 가선 안된다고 생각한다.

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

  • 필드가 가변 객체를 참조하거나, final이 아닌 인스턴스 필드를 public으로 선언하면 그 필드에 담을 수 있는 값을 제한할 힘을 잃게 된다.
  • 여기에 더해, 필드가 수정될 때 (락 획득 같은) 다른 작업을 할 수 없게 되므로 public 가변 필드를 갖는 클래스는 일반적으로 스레드 안전하지 않다.

길이가 0이 아닌 배열은 final 로 선언되어도 내부 element는 변경가능하니 주의하자.

  • 클래스에서 public static final 배열 필드를 두거나 이 필드를 반환하는 접근자 메서드를 절대 제공해선 안된다.
    • 왜냐하면 이를 사용하는 클라이언트 코드에서 그 배열의 내용을 수정할 수 있게 되게 때문에 final이 갖는 불변성을 못지키게 된다.
    • 예컨대 아래 코드는 보안 허점이 존재한다.
1
2
// 보안 허점이 숨어 있음
public static final Thing[] VALUES = {...};

위와 같은 보안 허점에 대한 해결책은 두 가지다.

1) public 배열을 private으로 만들고 불변 리스트를 추가하는 방법

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

Note: unmodifiableList와 관련해서 다음 포스팅을 참고하자.

2) public 배열을 private으로 만들고 그 복사본을 반환하는 public 메서드를 추가하는 방법

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

위 두 가지 방법 중에 클라이언트가 무엇을 원하느냐를 판단해 선택하여 사용하면 된다.

자바9의 모듈 시스템

  • 자바9에서는 모듈 시스템이라는 개념이 있다.
  • protected 혹은 public 멤버라도 해당 패키지를 공개하지 않았다면 모듈 외부에선 접근할 수 없다.
  • 물론 모듈안에선 exports로 선언했는지 여부에 아무런 영향도 받지 않는다.
  • JDK 외에도 모듈 개념이 널리 받아들여질지 예측하긴 아직 이른 감이 있으니 꼭 필요한 경우가 아니라면 당분간은 사용하지 않는게 좋다.

핵심정리: 프로그램 요소의 접근성은 가능한 한 최소한으로 하라. 꼭 필요한 것만 골라 최소한의 public API를 잘 설계하자. 그 외에는 클래스, 인터페이스, 멤버가 의도치 않게 API로 공개되는 일이 없도록 해야 한다. public 클래스는 상수용 public static final 필드 외에는 어떠한 public 필드도 가져서는 안 된다. public static final 필드가 참조하는 객체가 불변인지 확인하라.

This post is licensed under CC BY 4.0 by the author.

[Spring] Spring Data JPA 멀티 테넌시

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