-
이펙티브 자바 Item15 - 클래스와 멤버의 접근 권한을 최소화하라언어/Effective Java 2024. 5. 29. 17:28
잘 설계된 컴포넌트란?
- 캡슐화가 얼마나 잘 되었는지.
- 노출되는 API와 실제 구현이 얼마나 잘 분리되었는지.
- 메시지를 주고받는 두 컴포넌트가 서로의 내부 동작을 신경쓰지 않는지.
캡슐화를 잘 지켰을 때 장점
- 서로의 구현을 몰라도 되기 때문에 병렬로 개발이 가능하여 개발 속도가 빨라진다.
- 잘 분리되어있는 컴포넌트는 관리 포인트가 작다. 디버깅도 빠르고, 교체도 빠르다
- 잘 분리되어있는 컴포넌트는 최적화도 그 컴포넌트만 하면 되기 때문에 좋다.
- 외부 컴포넌트에 종속되지 않기 때문에 재사용성이 높다.
- 전체 시스템이 완성되지 않아도 개별 컴포넌트를 검증할 수 있기 때문에 큰 시스템 개발하는 난이도를 낮춰준다.
1. 캡슐화의 핵심은 "접근제어자"
공개 API를 설계하고 그것만 public으로 지정한다 > 나머지는 private으로 만든다. > 구현 중 같은 패키지의 다른클래스가 접근해야한다면 pakage-private로 풀어준다.
이 과정에서 너무 자주 푸러주는 일이 발생한다면 컴포넌트를 더 분해해야 하는 것일 수도 있다.
+ 추가 고려사항
1. 한 클래스에서만 사용하는 package-private 톱레벨 클래스나 인터페이스는 private 정적 클래스로 중첩시켜본다. 이 중첩 클래스는 포함된 톱레벨 클래스에서만 접근할 수 있다.
2.public 일 필요가 없는 클래스는 pakage-private클래스로 바꾸자
3. 코드를 테스트하려는 목적으로 접근제한을 풀어주면 안된다.
- 테스트 코드를 테스트 대상과 같은 패키기에 두면 package-private 요소까지 접근할 수 있게 된다.
4. 리스코프 치환원칙은 접근 제한을 방해하는 제약으로 작용한다.
- 상위 클래스의 인스턴스는 하위 클래스의 인스턴스로 언제든 대체해 사용할 수 있어야 한다는 규칙
- 이를 어길 경우 하위 클래스를 컴파일 할 때 컴파일 에러가 발생한다.
- 따라서 상위 클래스의 메서드를 재정의할 때는 접근수준을 상위 클래스보다 좁게 설정할 수 없다.
* Top Level 클래스와 인터페이스 접근제한자 : 가장 바깥 의미로 Top Level 클래스나 인터페이스에 부여할 수 있는 접근 수준은 package-private, public 두가지 이다. (package-private은 내부 구현이 되어 언제든 수정이 가능하고, 클라이언트에 피해없이 수정,교체, 제거가 가능하다.
private,package-private가 공개 API가 될 수 있는 "Serializable"구현
public class Member implements Seializable{ private static final long serialVersionID =1L; private String id; private String password; private String name; public Member(String id, String password, String name){ this.id= id; this.password = password; this.name = name; } } public class Member implements Serializable{ private static final long serialVersionID =1L; public String id; public String password; public String name }
- private인 Member 클래스를 직렬화한 뒤 -> public Member클래스로 역직렬화하면, private였던 password가 공개 API가 되었다.
- 공개 API의 제약
- 공개 API는 영원히 지원되어야 한다. 또 필요에따라 문서화되어야 한다. 그러므로 접근제어는 최대한 좁힐 수 있을 만큼 좁히자.
- 단, 리스코프 치환 원칙을 위해 하위클래스는 상위클래스보다 좁혀질 수 없다.
2. public 클래스의 인스턴스 필드가 public이 되어서는 안되는 이유
1. 변경에 매우 취약하다.
2. 스레드 안전하지 않다.
- 인스턴스 변수는 힙에 할당되는 공유자원이다. 즉 모든 스레드가 접근할 수 있다.
- public 인스턴스 변수는 Lock획득 같은 "thread-safe" 부가 작업을 할 수 없기 떄문에 사용하지말자
3. public final 필드의 문제점
- private 필드라면 내부 구현 변경 시 private 필드를 삭제할 수 있고, Getter든 Setter든 해당 클래스 내부만 수정하면 된다. 하지만 public final 필드의 경우 내부 구현 변경 시 이 필드가 직접 사용되고 있는 모든 소스를 찾아야한다.
- 유일하게 허용되는 public static final 필드는 추상 개념을 완성하는데 꼭 필요한 상수이다.
- 관용적으로 대문자 알파벳과 _조합으로 이루어져 있으며 기본타입 또는 불변객체를 참조해야한다.
4. 유일하게 허용되는 public static final 필드
- 추상 개념을 완성하는데 꼭 필요한 상수 (관용적으로 대문자알파벳과_조합으로 이루어져있으며, 기본 타입 혹은 불변객체 참조)
3. public 배열의 문제점
- final 키워드를 이용하더라도, 필드 멤버가 배열이라면 배열의 내부 값을 수정할 수 있다.
해결방법
1. private 배열과 public 불변리스트 추가
private static final Integer[] VALUES = {5, 3, 2}; public static final List<Integer> VALUES_LIST = Collections.unmodifiableList(Arrays.asList(VALUES));
2. 두 번째 해결방법: private 배열과 복사본 반환하는 public 메서드 추가 (방어적 복사)
private static final Integer[] VALUES = {5, 3, 2}; public static final Integer[] values(){ return VALUES.clone(); }
'언어 > Effective Java' 카테고리의 다른 글
이펙티브 자바 Item 17 - 변경 가능성을 최소화하라 (0) 2024.05.30 이펙티브 자바 Item 16 - public 클래스에서는 public필드가 아닌 접근자 메서드를 사용하라 (0) 2024.05.30 이펙티브 자바 Item 14 - Comparable을 구현할지 고려하라 (0) 2024.05.29 이펙티브 자바 Item 13 - clone 재정의는 주의해서 진행하라 (0) 2024.05.29 이펙티브 자바 Item 12 - toString은 항상 재정의하라 (0) 2024.05.29