kok202
[2019.03.10] 이펙티브 자바 3판 (4장 : 클래스, 인터페이스 설계)

2019. 3. 10. 04:18[공부] 독서/이펙티브 자바 3판

접근 제한자

모든 클래스의 멤버 접근성을 최대한 줄여라

public 클래스의 모든 멤버 변수는 private 이여야한다.

유일하게 허용되는 public 멤버 변수 : public staitc final 일 때 (단 멤버 변수가 배열이면 보안상의 문제가 존재하므로 허용하지 않는다.)

유일하게 허용되는 제한자 확장 : private 멤버를 package-private으로 풀어주는 경우

 

 

 

불변 클래스

불변 클래스를 만드는 방법

  1. setter를 제공하지 않는다.
  2. 상속을 못하게 막는다. (= final class로 선언한다.)
  3. 모든 변수를 private final로 선언한다.
  4. 가변 컴포넌트를 가지고 있다면 자신 외에 접근 못하게 막는다.

불변 객체의 장점

스레드의 공유자원으로서 안전하다.

불변 객체끼리는 내부 데이터가 교환 가능하다.

 

 

 

상속과 컴포지션

구체화 클래스의 상속보다는 컴포지션을 사용하라

  1. 다른 패키지의 특정 클래스의 동작 방식에 의존적인 코드를 짜게된다.
  2. 다른 패키지의 특정 클래스의 변화에 일일히 대응해야한다.
  3. 다른 패키지가 업데이트되서 내가 새로 정의한 메소드의 이름과 반환 타입이 같은 메소드가 추가된다면? 또 변경해야한다.

다른 패키지의 클래스를 상속하는 것은 캡슐화를 깨트리는 위험한 일이다.

인터페이스 상속은 괜찮다.

 

 

 

컴포지션 ( = 데코레이터 패턴 != 컴포지션 패턴)

public class ForwardingSet<E> implements Set<E> {}
public class MyHashSet<E> extends ForwardingSet<E> {}

ForwardingSet 클래스는 상위 클래스의 public 메소드를 오버라이딩하는데, 이 때 그 내용은 그냥 상위 클래스의 메소드를 그대로 반환하는 내용만 작성한다. (Delegator

 

ForwardingSet 클래스는 어댑터 같은 역할 ( != 어댑터 패턴 아님 그냥 순수한 의미의 어댑터 )

ForwardingSet 클래스를 Fowarding 클래스라고 부른다.

MyHashSet 클래스를 Wrapper 클래스라고 부른다.

 

 

 

상속용 클래스

상속용으로 설계한 클래스의 문서화는 필수다. 자신의 코드가 어떻게 돌아가는지 문서화 해야한다.

상속용으로 설계한 클래스의 생성자는 오버라이딩 될 메소드를 호출해서는 안된다. (중복 호출의 위험이 있다.)

상속용으로 설계한 클래스에 Cloneable, Serializable을 상속시키지마라

상속용으로 설계한 클래스에 private, final, static 메소드는 편히 사용하라. 이들은 오버라이딩이 불가능하다

 

 

 

상속을 금지시키는 법

클래스를 final class로 만든다.

생성자를 private으로 만든다.

 

 

 

추상 클래스보다 인터페이스로 만들어라

인터페이스가 혼합해서 사용하기 좋다.

메소드 일부는 구현해서 디폴트 메소드로 제공한다면 사용자가 사용하기 편해진다.

메소드 앞에 default 키워드를 붙이면 인터페이스 메소드에 구현체를 붙일 수 있다.

 

골격 구현 클래스를 같이 제공하면 (ex. java swing의 MouseAdapter) 더 좋다.

골격 구현 클래스의 이름 Prefix는 대부분 AbstractInterface 이다.

 

인터페이스의 수정은 구현체의 변경도 뒤따르는 일이다. 

디폴트 메소드 기능이 추가 덕분에 조금 더 유연해졌지만, 경우에 따라 디폴트 메소드가 기존 구현체의 런타임 오류를 발생시 킬 수도 있다. 따라서 인터페이스는 설계 단계에서부터 세심하게 해야한다는 점에는 변화가 없다.

 

 

 

인터페이스 설계시 안티 패턴

public static final 필드로 가득찬 상수.

상수의 선언은 내부 구현에 해당한다.

인터페이스는 타입을 정의하는 용도여야한다.

이렇게 되면 구현체들이 인터페이스의 상수에 종속적이게 된다.

* 숫자 리터럴에 언더바를 사용할 수 있다. ex) 3.141_592_653_586 

 

 

 

태그가 달린 클래스

클래스를 클래스 내부에 Enum 객체로 타입을 구분하고 있다면 클래스 계층 구조를 활용해라.

class Member {
    enum UserType{ADMIN, USER}
    private UserType userType;
}

이렇게 사용하지마라. (이를 태그가 달린 클래스라고 한다.)

여러 구현을 한 클래스가 혼합되어있고 switch, if 문이 마구잡이로 사용될 확률이 높다.

장황하기만하고 정말 쓸모없다.

 

 

 

중첩 클래스의 종류

  • 정적 멤버 클래스 : 내부 클래스가 바깥 클래스의 private에도 접근 가능
  • 비정적 멤버 클래스 : 내부 클래스가 바깥 클래스의 참조, 메소드를 가져올 수 있다.
  • 익명 클래스 : 말 그대로 이름이 없는 클래스. 일반적으로 함수형 인터페이스를 재정의 하는데 사용되는 경우가 많아서, 자바 7 이후 대부분 람다식으로 대체되어 사용된다.
  • 정적 팩토리 : 메소드에서도 사용된다. 지역 클래스 지역 변수처럼 어떤 Scope 안에 잠깐 생성됬다 사라지는 클래스

비정적 멤버 클래스를 쓸바에 무조건 정적 멤버 클래스로 만들어서 써라. 비정적 멤버 클래스의 인스턴스는 바깥 클래스의 인스턴스를 위한 외부 참조가 만들어지고 시간, 공간적 낭비다. 잘못하면 이 외부 참조 때문에 바깥 클래스의 소멸이 gc에도 안잡힌다.

그리고 하나의 자바 파일에는 top class 하나만 넣어놓아라. 이를 안지키면 컴파일 순서에 의존성이 생기고 프로그램 동작이 달라질 수 있다.