본문 바로가기

study/effective java

[item15] 클래스와 멤버의 접근 권한을 최소화하라

캡슐화의 장점: 정보은닉

캡슐화를 통해 얻는 대표적인 장점은 정보 은닉이다. 필요한 정보만 노출시키고 불필요한 정보를 숨겨서 객체의 자율성을 보장시킬 수 있다.

캡슐화를 통해 2가지 이점을 어을 수 있다.

  1. 데이터 보호: 외부로부터의 데이터 접근을 막아 데이터가 오염되는 것을 방지한다
  2. 데이터 은닉: 외부에서 책임을 요청할 때 필요한 정보만 명시하여 외부에서 내부의 자세한 동작 없이도 책임을 요청해 수행시킬 수 있다.

이팩티브자바의 현 챕터에서는 정보 은닉에 대한 관점을 이 내용을 서술한다. 이팩티브 자바에서 제시하는 정보 은닉을 통해 얻는 이점은 다음과 같다.

  1. 시스템 개발 속도를 높인다.
  2. 시스템 관리 비용이 낮아진다.
  3. 성능 최적화에 도움을 준다
  4. 재사용성을 높인다
  5. 시스템의 책임을 컴포넌트별로 분리하기에 시스템을 제작하는 난이도가 낮아진다.

이팩티브 자바에서는 정보 은닉의 장점을 최대한으로 얻기 위해 모든 클래스와 멤버의 접근성을 최소화해야한다고 제시한다.

접근 권한의 레벨

정보 은닉을 위한 접근 수준은 다음과 같다.

  1. private: 멤버를 선언한 최상위 클래스만 접근할 수 있다.
  2. package-private(default 클래스): 멤버가 소속된 패키지 내의 모든 클래스가 접근할 수 있다.
  3. protected: 이 멤버를 상속받는 다른 클래스도 사용할 수 있다.
  4. public: 모든 멤버가 접근할 수 있다.

이중에 대부분읊 private접근 수준을 사용해야한다. 만약 공개해야하는 상황이면 package-private 으로 풀어서 접근 수준을 최소화 해야한다. 만약 접근 권한을 자주 풀어줘야 한다면 이는 설계를 다시 해야한다는 신호이다.

package-private와 protected의 접근 수준의 차이는 상당히 크다. 그렇기에 protected 접근 수준 이상으로 올리는 것은 고민해야 한다. 접근 제한자는 한번 작성되고 배포되면 수정이 어렵기 때문이다.

가변 필드를 가지는 클래스는 Thread-safe하지 않다.

아래의 코드는 thread-safe하지 않는다.

public class MyClass{
    public static final int[] array=new int[]{1,2,3};
    public static final List<Integer> list=new LinkedList<>();
}

이 코드가 thread-safe하지 않는 이유는 array, list 참조 객체를 상수로 선언해 변경하지 못하는 것이지 위의 내부 값들은 수정이 가능하기 때문이다.

이럴 경우에는 2가지 방법으로 해결할 수 있다.

불변 리스트를 이용한다

값을 삽입, 수정을 못하는 불변 리스트를 만든다.

import java.util.Collections;
import java.util.List;

public class ImmutableCounter {
    private final List<Integer> counts;

    public ImmutableCounter(List<Integer> counts) {
        this.counts = Collections.unmodifiableList(counts);
    }

    public List<Integer> getCounts() {
        return counts;
    }
}

방어적 복사

객체 원본을 제공하지 않고 복제된 객체를 접근시킨다.

import java.util.ArrayList;
import java.util.List;

public class DefensiveCounter {
    private final List<Integer> counts;

    public DefensiveCounter(List<Integer> counts) {
        this.counts = new ArrayList<>(counts);
    }

    public List<Integer> getCounts() {
        return new ArrayList<>(counts);
    }
}

Reference