티스토리 뷰

728x90
반응형

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


  • 어설프게 설계된 컴포넌트와 잘 설계된 컴포넌트의 가장 큰 차이는 바로 클래스 내부 데아터와 내부 구현 정보를 외부 컴포넌트로부터 얼마나 잘 숨겼느냐입니다.
  • 잘 설계된 컴포넌트는 모든 내부 구현을 완벽히 숨겨, 구현과 API를 깔끔히 분리합니다. 오직 API를 통해서만 다른 컴포넌트와 소통하며 서로의 내부 동작 방식에는 전혀 개입하지 않습니다. 이렇게 구현과 API가 분리되어 있다면 사용자 입장에서는 API만 신경쓰면되고 그 결과에 집중하면 됩니다. 이런 개념을 정보 은닉과 캡슐화라고 합니다.

 

정보 은닉의 장점


  • 시스템 개발 속도를 높입니다. 여러 컴포넌트를 병렬적으로 개발할 수 있기 때문입니다.
  • 시스템 관리 비용을 낮춥니다. 각 컴포넌트를 빨리 파악하여 디버깅이 가능하고, 다른 컴포넌트로 교체하는 부담도 적기 때문입니다.
  • 성능 최적화에 도움을 줍니다. 정보 은닉 자체가 성능을 높혀 주는게 아니라, 완성된 시스템에서 최적화할 컴포넌트를 정하고 다른 컴포넌트에 영향을 주지 않고 해당 컴포넌트만 최적화를 할 수 있기 때문입니다.
  • 소프트웨어 재사용성이 높아집니다. 외부에 거의 의존하지 않고 독자적으로 동작이 가능한 컴포넌트는 다른 시스템에도 바로 적용이 가능하기 때문입니다.
  • 큰 시스템을 제작하는 난이도를 낮춥니다. 시스템이 완성되지 않은 상태에서도 개별 컴포넌트의 동작을 검증할 수 있기 때문입니다.

 

모든 클래스와 멤버의 접근성을 가능한 좁히자


  • 모든 클래스, 인터페이스, 멤버 변수 및 메서드에 대해 접근성을 최대한 좁히면 해당 데이터를 수정할 수 있는 대상이 줄어듭니다. 그렇게 되면 접근성이 낮아져 독립적이고, 위/변조의 위험으로부터 안전해질 수 있습니다.
  • 클래스와 인터페이스에는 package-private(default), public을 부여할 수 있습니다. public으로 부여할 경우 공개 API가 되며, package-private로 하는 경우에는 해당 패키지 내에서만 사용할 수 있습니다. 그렇기 때문에 해당 클래스를 패키지 외부에서 사용할 일이 없다면 package-private로 선언하는 것이 관리측면에서 좋습니다.

 

접근 제한자의 범위


  • private - 해당 클래스내에서만 접근 가능
  • package-private - 같은 패키지 안의 모든 클래스에서 접근 가능. 접근 제한자를 명시하지 않을 경우 기본적으로 package-private이며, 인터페이스는 public이 기본
  • protected - package-private의 접근 범위를 포함하며, 이 접근 제한자를 선언한 클래스는 상속받은 하위 클래스에서도 접근 가능
  • public - 모든 곳에서 접근 가능

 

접근 제한자를 좁히지 못하는 경우


  • 상위 클래스의 메서드를 하위 클래스에서 재정의하는 경우 해당 메서드의 접근 범위를 상위 클래스에 선언된 접근 범위보다 좁게 설정할 수 없습니다. 예를 들어 상위 클래스에서 package-private인 경우 하위 클래스에서는 package-private, protected, public으로만 설정할 수 있습니다.
  • 이유는 상위 클래스의 인스턴스는 하위 클래스의 인스턴스로 대체해 사용할 수 있어야한다는 리스코프 치환 원칙에 의해서 입니다.

 

public 클래스의 인스턴스 필드는 되도록 public 접근 제한자 아니어야 한다


  • 필드가 가변 객체를 참조하거나, final이 아닌 인스턴스 필드를 public으로 선언하면 해당 필드를 제한할 수 없어지며, 해당 필드에 대한 모든 것은 불변식을 보장할 수 없게됩니다.
  • 필드가 수정될 때 막을 수 없기 때문에 public 가변 필드를 갖는 클래스는 일반적으로 스레드에 안전하지 않습니다.
  • 단 상수라면 public static final 필드로 공개해도 좋습니다.
public class DiscountPolicy {

    public static final int DISCOUNT_AMOUNT = 1000;
}

 

🧨 단 public static final 필드이더라도 가변 객체를 참조한다면 final이 아닌 필드에 적용되는 모든 불이익이 그대로 적용됩니다.

final로 선언하더라도 참조된 객체 자체가 수정될 수 있기 때문입니다. 또한 길이가 0이 아닌 배열이나 컬렉션들도 변경이 가능하니 주의해야 합니다.

아래 배열 필드는 final이여도 해당 값을 참조하는 곳에서 배열 내부의 값은 얼마든지 수정할 수 있습니다.

public static final Thing[] VALUES = {...};

 

💡해결 방법

  • 접근 제한자를 public 대신 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();
}

 

 

 

 

728x90
반응형