아이템 2: 생성자에 매개변수가 많다면 빌더를 고려하라.

rescogitans·2022년 4월 4일
0

이펙티브 자바

목록 보기
2/5
  • 정적 팩터리와 생성자의 공통 제약사항: 선택적 매개변수가 많을 경우 대응이 어렵다.

선택 매개변수가 많을 때 대응법

  1. 점층적 생성자 패턴(telescoping constructor pattern)
public class NutritionFacts {
    private final sevingSize;
    private final servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    
    public NutritionFacts(int servingSize, int servings) {
    this(servingSize, servings, 0);
    }
    
    public NutritionFacts(int servingSize, int servings, int calories) {
    this(servingSize, servings, calories, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories, int fat) {
    this.servingSize = servingSize;
    this.servings = servings;
    this.calories = calories;
    this.fat = fat;
    }
}
  • 설정하길 원하지 않는 매개변수에도 값을 지정해줘야 함
  • 코드 가독성이 낮고 수정 및 사용이 어려움
  1. 자바빈즈 패턴(JavaBeans pattern)
  • 기본 생성자로 객체 생성 후 Setter로 값 설정
  • 단점
    • 객체 하나를 생성하기 위해 Setter를 여러 번 호출해야 한다.
    • 객체가 완전히 생성되기 전까지는 일관성(consistency)이 무너진 상태에 놓인다.
      • 때문에 클래스를 불변으로 만들 수 없으며
      • 스레드 안전성을 얻기 위해 추가 작업이 필요하다.
    • freeze 메서드
      • 생성이 끝난 객체를 얼리며(freeze) 얼리기 전에는 사용하지 못 하게 만들어 위의 단점을 완화하려는 기법
      • 하지만 freeze 메서드를 객체 사용 전에 호출하였는지를 컴파일 단계에서 검증 불가하기에 런타임 오류에 취약하다.
  1. 빌더 패턴
  • 점층적 생성자 패턴의 안전성과 자바빈즈 패턴의 가독성을 갖춤

빌더 패턴(Builder Pattern)

  • 작동방식

    • 필수 매개변수만으로 생성자 / 정적 팩터리를 호출하여 빌더 객체를 얻는다.
    • 빌더 객체의 설정 메서드(Setter와 유사)로 선택 매개변수를 설정한다.
    • 매개변수가 없는 build 메서드를 호출해 필요한 (일반적으로는 불변) 객체를 얻는다.
  • 빌더는 생성할 클래스 안에 정적 멤버 클래스로 만들어두는 것이 보통이다.

  • Builder의 Setter 메서드

    • 자기 자신을 반환하므로 메서드 연쇄 가능
      • 메서드 연쇄(method chaining) = 플루언트 API(fluent API)
    • 메서드명을 속성명으로 하면 가독성이 좋은 코드를 짤 수 있다:
    • NutritionFacts cocacola = NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();
    • 빌더 패턴은 파이썬/스칼라의 명명된 선택적 매개변수(named optional parameters)를 흉내낸 것이다.
  • 유효성 검사 코드

    • 빌더의 생성자와 메서드에서 입력 매개변수를 검사

    • build 메서드가 호출하는 생성자 = NutritionFacts(Builder builder)에서 여러 매개변수에 걸친 불변식(invariant)을 검사

      • 빌더로부터 매개변수를 복사한 후 해당 객체 필드도 검사한다.
      • 문제 발견시 IllegalArgumentException

불변과 불변식

  • 불변(immutable or immutability)
    • 변경을 허용하지 않는다. <-> 가변(mutable) 객체
      • Ex) String
  • 불변식(invariant)
    • 프로그램이 실행되는 동안 or 정해진 기간 동안 반드시 만족해야 하는 조건
    • 변경 허용이 가능하지만 조건 내에서만 가능
      • Ex) List의 크기는 0 이상, Period의 start필드 값은 end 필드 값보다 앞서야 함
  • 가변 객체에도 불변식은 존재할 수 있으며 불변은 불변식의 극단적인 예라고 할 수 있다.

빌더와 계층적 클래스

  • 각 계층의 클래스에 관련 빌더를 멤버로 정의
    • 추상 클래스는 추상 빌더를, 구체 클래스(concrete class)는 구체 빌더를 갖게 함
    • 시뮬레이트한 셀프 타입(simulated self-type) 관용구: self 타입이 없는 자바에서 self메서드 정의하여 사용
      • 이를 통해 형변환 없이 메서드 연쇄 지원
    • 공변 반환 타이핑(covariant return typing): 하위 클래스의 메서드가 상위 클래스의 메서드가 정의한 반환 타입이 아닌, 그 하위 타입을 반환하는 기능
      • 이를 통해 형변환 없이 빌더 사용 가능

그 외

  • 빌더를 사용하면 가변인수(varargs) 매개변수를 여러 개 사용할 수 있다.

    • 각각을 적절한 메서드로 나눠 선언하거나
    • 메서드를 여러 번 호출하도록 하고 각 호출 때 남겨진 매개변수들을 하나의 필드로 모음
  • 빌더는 매우 유연함

  • 단점

    • 객체 생성 이전에 빌더를 만들어야 함

      • 경우에 따라 성능에 부정적 영향
    • 매개변수가 많을 때에나 빛을 발함

      • 하지만 매개변수는 점차 늘어나는 법이기에 빌더로 애초에 만드는 것이 좋을 때가 많다.

0개의 댓글