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

gang_shik·2022년 3월 27일
0

Effective Java 4장

목록 보기
1/11
  • 잘 설계된 컴포넌트는 모든 내부 구현을 완벽히 숨겨 구현과 API를 깔끔히 분리함

  • 오직 API를 통해서만 다른 컴포넌트와 소통하며 서로의 내부 동작 방식에는 전혀 개의치 않음, 정보 은닉 혹은 캡슐화라고 하는 이 개념은 소프트웨어 설계의 근간이 되는 원리임

정보 은닉

  • 정보은닉은 시스템을 구성하는 컴포넌트들을 서로 독립시켜서 개발, 테스트, 최적화, 적용, 분석, 수정을 개별적으로 할 수 있게 해주는 것과 연관이 있음

  • 장점은 여러가지 존재함

    • 시스템 개발 속도를 높임, 여러 컴포넌트를 병렬로 개발할 수 있음
    • 시스템 관리 비용을 낮춤, 각 컴포넌트를 더 빨리 파악하여 디버깅할 수 있고 다른 컴포넌트로 교체하는 부담도 적음
    • 성능 최적화에 도움을 줌, 완성된 시스템을 프로파일링해 최적화할 컴포넌트를 정한 다음, 다른 컴포넌트에 영향을 주지 않고 해당 컴포넌트만 최적화할 수 있음
    • 소프트웨어 재사용성을 높임, 외부에 거의 의존하지 않고 독자적으로 동작할 수 있는 컴포넌트라면 그 컴포넌트와 함께 개발되지 않은 낯선 환경에서도 유용하게 쓰일 가능성이 큼
    • 큰 시스템을 제작하는 난이도를 낮춤, 시스템 전체가 아직 완성되지 않은 상태에서도 개별 컴포넌트의 동작을 검증할 수 있음
  • 자바는 정보 은닉을 위한 다양한 장치를 제공함, 그 중 접근 제어 메커니즘은 클래스, 인터페이스, 멤버의 접근성을 명시함

  • 각 요소의 접근성은 그 요소가 선언된 위치접근 제한자로 정해짐, 이것을 제대로 활용하는 것이 핵심임

기본 원칙

  • 모든 클래스멤버의 접근성을 가능한 한 좁혀야 함, 즉 소프트웨어가 올바로 동작하는 한 항상 가장 낮은 접근 수준을 부여해야 함

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

    • 톱레벨 클래스나 인터페이스를 public으로 선언하면 공개 API가 됨
    • package-private로 선언하면 해당 패키지 안에서만 이용할 수 있음
  • 패키지 외부에서 쓸 이유가 없다면 package-private 선언이 나음

    • API가 아닌 내부 구현이 되어 언제든 수정할 수 있음
    • 클라이언트에 아무런 피해 없이 다음 릴리스에서 수정, 교체, 제거할 수 있음
  • public으로 선언시, API가 되므로 하위 호환을 위해 영원히 관리해줘야함

  • 한 클래스에서만 사용하는 package-private 톱레벨 클래스나 인터페이스는 이를 사용하는 클래스 안에 private static 으로 중첩시켜보자

    • 톱레벨로 두면 같은 패키지의 모든 클래스가 접근할 수 있지만, private static 으로 중첩시키면 바깥 클래스 하나에서만 접근할 수 있음
  • public 일 필요가 없는 클래스의 접근 수준을 packaga-private 톱레벨 클래스로 좁히는 일

    • public 클래스는 그 패키지의 API인 반면, package-private 톱레벨 클래스는 내부 구현에 속함

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

  • 필드, 메서드, 중첩 클래스, 중첩 인터페이스 등에 부여할 수 있는 접근 수준

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

그 외

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

  • privatepackage-private 멤버는 모두 해당 클래스의 구현에 해당하므로 보통은 공개 API에 영향을 주지 않음(단, Serializable을 구현한 클래스에서는 그 필드들도 의도치 않게 공개 API가 될 수도 있음)

  • public 클래스에서는 멤버의 접근 수준을 package-private 이나 protected 로 바꾸는 순간 그 멤버에 접근할 수 있는 대상 범위가 엄청나게 넓어짐

  • public 클래스의 protected 멤버는 공개 API이므로 영원히 지원돼야 함, 또한 내부 동작 방식을 API 문서에 적어 사용자에게 공개해야 할 수도 있음, 따라서 protected 멤버의 수는 적을수록 좋음

제약

  • 상위 클래스의 메서드를 재정의할 때는 그 접근 수준을 상위 클래스보다 좁게 설정할 수 없음, 이 규칙은 상위 클래스의 인스턴스는 하위 클래스의 인스턴스로 대체해 사용할 수 있는 규칙(리스코프 치환 원칙)을 지키기 위해서 필요함, 이 규칙을 어기면 컴파일 오류가 남

  • 클래스가 인퍼테이스를 구현하는 건 이 규칙의 특별한 예이고 이때 클래스는 인터페이스가 정의한 모든 메서드를 public 으로 선언해야함

테스트

  • 단지 코드를 테스트하려는 목적으로 클래스, 인터페이스, 멤버의 접근 범위를 적당한 수준까진 넓혀도 됨

원칙

  • public 클래스의 인스턴스 필드는 되도록 public 이 아니어야 함

    • 필드가 가변 객체를 참조하거나, final이 아닌 인스턴스 필드를 public으로 선언하면 그 필드에 담을 수 있는 값을 제한할 힘을 잃게됨(그 필드와 관련된 모든 것은 불변식을 보장할 수 없게 된다는 뜻)
  • 필드가 수정될 때 다른 작업을 할 수 없게 되므로 public 가변 필드를 갖는 클래스는 일반적으로 스레드 안전하지 않음

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

    • 관례상 이런 상수의 이름은 대문자 알파벳으로 쓰며, 각 단어 사이에 밑줄(_)을 넣음
    • 이런 필드는 반드시 기본 타입 값이나 불변 객체를 참조해야함
  • 길이가 0이 아닌 배열은 모두 변경 가능하니 주의하자

    • 클래스에서 public static final 배열 필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공해서는 안됨

    • 이러면 클라이언트에서 내용 수정이 가능하고 보안 허점이 생김

      public static final Thing[] VALUES = { ... };
    • 이를 해결하기 위한 방법은 2가지가 존재함

      • 앞 코드의 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();
        }
    • 둘 중 나은 방식에 대해서 나은 쪽을 생각하면 됨

추가

  • 자바 9에서는 모듈 시스템이라는 개념이 도입되어서 암묵적 접근 수준이 추가됨

  • 모듈은 패키지들의 묶음임, 모듈은 자신에 속하는 패키지 중 공개(export)할 것들을 선언함

  • protected 혹은 public 멤버라도 해당 패키지를 공개하지 않았다면 모듈 외부에서는 접근할 수 없음

  • 모듈 시스템을 활용하면 클래스를 외부에 공개하지 않으면서도 같은 모듈을 이루는 패키지 사이에서는 자유롭게 공유할 수 있음

모듈 시스템?

모듈 시스템

일반적으로 프로젝트를 만들면 아래와 같은 구조로 만들어짐

프로젝트명대로 만들어지고 그 하위에 패키지를 만들고 클래스를 만들고 작업 처리를 함

모듈 시스템을 통해서 아이템 15에서 말한대로 접근 권한을 최소화하고 분리를 시켜서 나눈다는 것이 시스템적으로 모듈로 묶어서 처리하는 것임

즉 위의 사진으로 따지면 src 하위의 다양한 패키지를 바탕으로 클래스를 나누지만 여기서 모듈을 활용한다면 Algorithm외에 또 하나의 위와 같은 패키지 구조를 가질 수 있는 모듈을 더 생성해서 이를 통해서 분리를 하는 것임

이는 그러면 아이템 15에서 계속 말한대로 정보은닉, 캡슐화 하는 서로 독립시켜 개발하는 이 독립성을 더더욱 보장 받을 수 있음

일례로 안드로이드 역시 이와 같은 프로젝트 구조를 사용해서 예를 든다면 app모듈 안에서 여러 컴포넌트 요소들을 만들고 패키지구조를 분리해서 관리하지만 이를 더 확실하게 독립시키고 처리하기 위해서 레이어 단위로 모듈을 분리해서 처리할 수 있음

즉, 이는 View만을 처리하는 레이어, Data만을 관리해서 처리하는 레이어로 각각 모듈로 관리함으로써 해당 모듈에는 딱 해당 관심사에 대해서만 관리하고 코딩을 하는 것인데 이는 어찌보면 아이템 15에서 말한 주장에서의 더 효율적인 관리가 될 수 있음

그리고 이 모듈들의 경우 완벽히 독립되어 있음과 동시에 이를 사용하는데 있어서 의존성 추가를 통해서 서로 참조를 할 수 있고 이는 아이템 15 마지막에서 말하는 모듈을 활용해 더더욱 아이템 15에서 주장하는 바를 더더욱 효과적으로 사용할 수 있음(물론 규모가 그 정도로 크지 않다면 오히려 비효율적일 수도 있음)

profile
측정할 수 없으면 관리할 수 없고, 관리할 수 없으면 개선시킬 수도 없다

0개의 댓글