-
Notifications
You must be signed in to change notification settings - Fork 2
Open
Labels
🐸 04 Classes & Interfaces4장 클래스와 인터페이스4장 클래스와 인터페이스
Description
Chapter : 4. 클래스와 인터페이스
Item : 15. 클래스와 멤버의 접근 권한을 최소화하라
Assignee : byunghyunkim0
🍑 서론
잘 설계된 컴포넌트는 내부 구현을 숨기고, 구현과 API를 깔끔하게 분리한다. 오직 API를 통해서만 다른 컴포넌트와 소통하며 서로의 내부 동작 방식에 영향을 주지 않는다.
정보 은닉의 장점
- 여러 컴포넌트를 병렬로 개발할 수 있기 때문에 개발 속도를 높여준다.
public interface MemberService { void joinMember(Member member); List<Member> getMembers(); void leaveMember(Member member); }
- 분업을 통해서 각각 joinMember, getMembers, leaveMember 기능을 구현한다.
- 시스템 관리 비용을 낮춘다.
- 각 컴포넌트를 더 빨리 파악하여 디버깅을 할 수 있고, 다른 컴포넌트로 교체하는 부담도 적어진다.
- 성능 최적화에 도움을 준다.
- 다른 컴포넌트에 영향을 주지 않고 알고리즘의 변경, 아키텍처 변경과 같은 최적화를 해당 컴포넌트만 진행할수있다.
- 소프트웨어 재사용성을 높인다.
- 컴포넌트가 외부에 거의 의존하지 않고 독자적으로 동작한다면 낯선 환경에서도 유용하게 쓰일 가능성이 높다.
- 시스템 제작 난이도를 낮춰준다.
- 시스템 전체가 아직 완성되지 않은 상태에서도 개별 컴포넌트의 동작을 검증할 수 있기 때문이다.
🍑 본론
접근 제한자
- private : 멤버를 선언한 톱레벨 클래스에서만 접근 가능
- package-private : 멤버가 소속된 패키지 안의 모든 클래스에서 접근할 수 있다. 제한자를 명시하지 않았을 때 적용(default)
- protected : package-private의 접근 범위를 포함. 이 멤버를 선언한 클래스의 하위 클래스에서도 접근할 수 있다.
- public : 모든 곳에서 접근 가능
기본 원칙
- 모든 클래스와 멤버의 접근성을 가능한 좁혀야 한다.
- 톱 레벨 클래스와 인터페이스에 부여할 수 있는 접근 수준은 package-private와 public 두가지이다.
- public으로 선언하면 공개 api, package-private로 선언하면 해당 패키지 안에서만 이용가능하다.
- 패키지 외부에서 쓸 이유가 없다면 package-private로 선언하도록한다.
- public으로 선언한다면 api가 되므로 하위 호환을 위해 영원히 관리해줘야만 한다.
클래스 접근 범위
- 클래스의 공개 API외의 모든 멤버는 private으로 만들자
- 같은 패키지의 다른 클래스가 접근해야 하는 멤버에 한하여 package-private로 풀어준다.
- package-private로 권한은 풀어주는 일을 많이 하게 된다면 컴포넌트를 더 분해하도록 고민하는것이 좋다.
- private, package-private 멤버는 클래스의 구현에 해당하므로 공개 API에 영향을 주지 않는다. 단, Serializable을 구현한 클래스에서는 의도치 않게 공개 API가 될 수도 있다.
public클래스의 멤버 접근 수준
- package-private에서 protected로 바꾼다면 접근할 수 있는 대상 범위가 엄청나게 넓어진다.
- public 클래스의 protected 멤버는 공개 API이므로 영원히 지원돼야 한다.
- protected 멤버의 수는 적을수록 좋음
멤버 접근성 제약
- 상위 클래스의 메서드를 재정의할 때는 그 접근 수준을 상위 클래스에서보다 좁게 설정 할 수 없다 (리스코프 치환 원칙)
public class Parent {
public void test() {
}
}
class Child extends Parent {
// 더 좁은 범위로 사용하였기 때문에 컴파일 오류
@Override
protected void test() {
super.test();
}
}- 단지 코드를 테스트하려는 목적으로 클래스, 인터페이스, 멤버의 접근 범위를 넓히려 할때는 적당한 수준까지는 넓혀도 괜찮다.
- public 클래스의 private 멤버를 package-private까지 허용
- 즉, 테스트만을 위해 클래스, 인터페이스, 멤버를 공개 API로 만들어서는 안된다.
주의점
public 클래스의 인스턴스 필드는 되도록 public이 아니어야 한다.
- 필드가 가변 객체를 참조하거나, final이 아닌 인스턴스 필드를 public으로 선언하면 값을 제한할 힘을 잃게 된다. (필드와 관련된 모든 것에 불변식을 보장할 수 없게 된다.)
- 필드가 수정될 때 다른 작업을 할 수 없게 되므로 public 가변 필드를 갖는 클래스는 일반적으로 thread-safety하지 않다.
- 예외적으로 상수에 대해서 public static final 필드로 공개해도 좋다.
- 관례상 이름을 대문자 알파벳으로 쓰고, 각 단어 사이에 밑줄(_)을 넣는다.
- 반드시 기본 타입 값이나 불변 객체를 참조해야 한다.
클래스에서 public static 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();
}모듈 (패키지들의 묶음)
- 자바 9부터 모듈 시스템이 도입되었고, 두 가지 암묵적 접근 수준이 추가되었다.
- 패키지 중 공개(export)할 것들을 관례상 module-info.java파일에 선언한다.
- protected, public 멤버라도 해당 패키지를 공개하지 않았다면 모듈 외부에서 접근할수 없다.
- 모듈의 JAR 파일을 자신의 모듈 경로가 아닌 애플리케이션 클래스패스에 두면 그 모듈 안의 모든 패키지는 모듈이 없는 것처럼 행동한다. 공개 여부와 상관없이 public클래스의 public, protected 멤버를 모듈 밖에서도 접근할 수 있게 된다. (대표적인 예가 JDK)
🍑 결론
- 프로그램 요소의 접근성은 가능한 최소한으로 해라.
Referenced by
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
🐸 04 Classes & Interfaces4장 클래스와 인터페이스4장 클래스와 인터페이스