반응형
정보 은닉(캡슐화)이란?
내부의 데이터와 구현을 외부로 부터 숨기는 것
→ 이를 통해, A는 자신의 역할만 잘 수행하도록 하고, A를 사용하는 B는 A가 내부적으로 어떻게 수행되는지에는 알 필요가 없다
정보은닉의 장점
- 시스템 개발속도 향상 → 여러 컴포넌트를 병렬로 개발
- 시스템 관리비용을 낮춘다. → 각 컴포넌트를 빨리 디버깅가능, 교체 용이
- 정보 은닉 자체가 성능을 높여주지는 않지만, 성능 최적화에 도움 → 다른 컴포넌트에 영향을 주지않고 특정 컴포넌트만 최적화 가능
- 소프트웨어 재사용성 향상 → 의존성 없다면 다른곳에서 재사용 가능
- 큰 시스템 제작 난이도 낮춘다 → 개별 컴포넌트 동작 검증 가능
클래스와 인터페이스의 접근 제한자 사용 원칙
- 모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 한다.
- 톱레벨 클래스와 인터페이스에 package-private 또는 public 을 쓸 수 있다.
- public 으로 선언하면 하위 호환성을 유지하려면 영원히 관리해야한다.
- 패키지 외부에서 쓰지 않을 클래스나 인터페이스라면package-private으로 선언한다. (인터페이스는 클래스 레벨에서는 public or package-private(default) 만사용 가능)
- 한 클래스에서만 사용하는 package-private 클래스나 인터페이스는 해당 클래스에private static 으로 중첩시키자 → static class를 만들어서 사용하자는 의미
public class A {
private int a;
}
public class B {
private int b;
}
B가 A에서만 사용되는 클래스라면 ? → inner 클래스로 static class를 만들어서 사용
public class A {
private int a;
private static class B {
private int b;
}
}
멤버의 접근 제한자의 종류
- private : 해당 클래스만 접근 가능
- package-private(default) : 같은 패키지에서 접근 가능
- 접근 제한자를 명시하지 않을 때 적용되지만 인터페이스의 멤버는 기본적으로 public이 적용된다
- protected : 같은 패키지 + 하위 클래스에서 접근 가능
- public : 모든 곳에서 접근 가능
접근 제한자 설정 과정
private → package-privagte → protected → public 처럼 일단 접근성을 좁게 하여 선언한 뒤에 권한을 점점 풀어주는 방식으로 설정한다. 권한을 풀어주는 일이 자주 발생한다면 컴포넌트를 더 분해해야하는 것이 아닌지 고민해야 한다.
반대로 멤버 접근성을 좁히는 지 못하는 제약이 존재할 수 있는데
- 하위 클래스에서 재정의하는 메소드의 접근성 ≥ 상위 클래스에서의 접근성인 경우
public class Parent {
protected void something() {}
}
public class Child extends Parent {
@Override
void something(){} //컴파일 오류
}
public class ChildTwo extends Parent {
@Override
public void something(){} //가능
}
이런 제약이 있는 이유 → LSP (liskov-subsitution-principle) 때문
리스코프 치환 원칙이란?
상위 클래스의 인스턴스는 하위 클래스의 인스턴스로 대체해 사용할 수 있어야 한다. (자식 클래스는, 최소한 자신의 부모 클래스에서 가능한 행위는 수행 할 수 있어야 한다.)
Parent p = new ChildTwo();
/*상위 클래스에 정의된 메소드라면 호출할 수 있던게, 하위 클래스 재정의로 인해서
호출 할 수없게 되면 안된다.*/
p.something();
반응형
코드를 테스트 할 때 접근 범위
- 적당한 수준까지는 넓혀도 된다
- 예를 들어, public 클래스의 private 멤버를 package-private까지 풀어주는 것은 허용할 수 있다
- 하지만, 그이상은 안된다
- 즉, 테스트만을 위해 클래스, 인터페이스 멤버를 공개 API로 만들어서는 안된다 (@VisibleForTesting 를 사용할 수도 있다)
public 클래스의 인스턴스 필드는 되도록 public이 아니여야 한다
- 필드가 가변 객체를 참조하거나, final이 아닌 인스턴스 필드를 public으로 선언하면 해당 필드와 관련된 것들은 불변식을 보장 할 수 없다.
- 값을 언제든 바꿀수 있다보니 멀티 스레드 환경에서 사용하려면 별도의 동기화 처리가 필요하게 된다. → 스레드 안전하지 않게 된다.
- 예외의 경우 static final 상수를 public으로 지정할 수 있다
- 클래스에서 public static final 배열 필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공해서는 안된다
- 배열의 내용이 수정 가능해지므로만약 어쩔수 없이 한다면 밑의 2가지 방식으로 표현해야 한다
// 보안 허점이 존재
public static final Thing[] VALUES = {...};
// 1번째 방법 : public을 private로 만들고 public 불변 리스트를 추가한다.
private static final Thing[] PRIVATE_VALUES = {...};
public static final List<Thing> VALUES =
Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
// 2번째 방법 : 배열을 private로 만들고 복사본을 반환하는 public 메서드를 추가한다.(방어적복사)
private static final Thing[] PRIVATE_VALUES = {...};
public static final Thing[] values(){
return PRIVATE_VALUES.clone();
}
정리
- 접근성은 최소화으로 유지하도록 하자
- public 클래스는 상수용(public static final)필들 외에는 어떠한 public 필드도 가져서는 안된다
- public static final필드가 참조하는 객체가 불변인지 확인해라
반응형
'JAVA > 이펙티브자바' 카테고리의 다른 글
이펙티브자바 Item16. public클래스에는 public필드가 아닌 접근자 메서드를 사용하라 (0) | 2024.10.07 |
---|---|
이펙티브자바 Item2. 생성자에 매개변수가 많다면 빌더를 고려하라 (1) | 2024.02.18 |
이펙티브자바 Item1. 생성자 대신 정적 팩터리 메서드를 고려하라. (0) | 2024.02.18 |