# 아이템15 클래스와 멤버의 접근 권한을 최소화하라

어설프게 설계된 컴포넌트와 잘 설계된 컴포넌트의 가장 큰 차이는 바로 `클래스 내부 데이터와 내부 구현 정보`를 외부 컴포넌트로부터 얼마나 잘 숨겼느냐이다.

### 정보 은닉의 장점을 구체적으로 한번 알아보자.

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

### 기본 원칙은 간단하다.

모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 한다. 달리 말하면, 소프트웨어가 올바로 동작하는 한 항상 `가장 낮은 접근 수준`을 부여해야 한다는 뜻이다.

### 멤버에 부여할 수 있는 접근 수준

* private : 멤버를 선언한 톱 레벨 클래스에서만 접근할 수 있다.
* package-private : 멤버가 소속된 패키지 안의 모든 클래스에서 접근할 수 있다.
* protected : package-private의 접근 범위를 포함하며 이 멤버를 선언한 클래스의 하위 클래스에서도 접근할 수 있다.
* public : 모든 곳에서 접근할 수 있다.

클래스의 공개 API를 세심히 설계한 후, `그 외의 모든 멤버는 private으로 만들자.` 그런 다음 오직 같은 패키지의 다른 클래스가 접근해야 하는 멤버에 한하여 (private 제한자를 제거해) `package-private`으로 풀어주자

그런데 멤버 접근성을 좁히지 못하게 방해하는 `제약`이 하나 있다. 상위 클래스의 메서드를 `재정의`할 때는 그 접근 수준을 상위 클래스 에서 보다 좁게 설정할 수 없다는 것이다. 이 제약은 상위 클래스의 인스턴스는 하위 클래스의 인스턴스로 대체해 사용할 수 있어야 한다는 규칙(`리스코프 치환 원칙`)을 지키기 위해 필요하다.

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

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

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

길이가 0이 아닌 배열은 모두 변경 가능하니 주의하자. 따라서 클래스에서 public static final 배열 필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공해서는 안 된다. 이런 필드나 접근자를 제공한다면 클라이언트에서 그 배열의 내용을 수정할 수 있게 된다. 예컨대 다음 코드에는 보안 허점이 존재한다.

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

### 해결책은 두 가지다.

첫 번째 방법은 앞 코드의 `public` 배열을 `private`으로 만들고 `public 불변 리스트`를 추가하는 것이다.

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

두 번째는 배열을 `private`으로 만들고 그 복사본을 `반환하는 public 메서드`를 추가하는 방법이다(방어적 복사)

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

자바 9에서는 `모듈 시스템`이라는 개념이 도입되면서 `두 가지 암묵적 접근 수준`이 추가되었다. 패키지가 `클래스들의 묶음`이듯, 모듈은 `패키지들의 묶음`이다.

모듈 시스템에서 도입도니 두 가지 암묵적 접근 수준이란?

앞서 다룬 4개의 기존 접근 수준과 달리, 모듈에 적용되는 새로운 두 접근 수준은 상당히 주의해서 사용해야 한다. 여러분 모듈의 JAR 파일을 자신의 모듈 경로가 아닌 애플리케이션의 `클래스패스(classpath)`에 두면 그 모듈 안의 모든 패키지는 마치 모듈이 없는 것처럼 행동한다. 즉, 모듈이 공개 했는지 여부와 상관없이, `public 클래스가 선언한 모든 public 혹은 protected 멤버를 모듈 밖에서도 접근할 수 있게 된다.` 새로 등장한 이 접근 수준을 적극 활용한 대표적인 예가 바로 `JDK` 자체다. 자바 라이브러리에서 공개하지 않은 패키지들은 해당 모듈 밖에서는 절대로 접근할 수 없다.

JDK 외에도 모듈 개념이 널리 받아들여질지 예측하기는 아직 이른 감이 있다. 그러니 꼭 필요한 경우가 아니라면 당분간은 사용하지 않는 게 좋을 것 같다.

## 정리

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