ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 이펙티브 자바 Item15 - 클래스와 멤버의 접근 권한을 최소화하라
    언어/Effective Java 2024. 5. 29. 17:28

    잘 설계된 컴포넌트란?

    1. 캡슐화가 얼마나 잘 되었는지.
    2. 노출되는 API와 실제 구현이 얼마나 잘 분리되었는지.
    3. 메시지를 주고받는 두 컴포넌트가 서로의 내부 동작을 신경쓰지 않는지.

     

    캡슐화를 잘 지켰을 때 장점

    1. 서로의 구현을 몰라도 되기 때문에 병렬로 개발이 가능하여 개발 속도가 빨라진다.
    2. 잘 분리되어있는 컴포넌트는 관리 포인트가 작다. 디버깅도 빠르고, 교체도 빠르다
    3. 잘 분리되어있는 컴포넌트는 최적화도 그 컴포넌트만 하면 되기 때문에 좋다.
    4. 외부 컴포넌트에 종속되지 않기 때문에 재사용성이 높다.
    5. 전체 시스템이 완성되지 않아도 개별 컴포넌트를 검증할 수 있기 때문에 큰 시스템 개발하는 난이도를 낮춰준다.

    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();
    }

     

Designed by Tistory.