Item 2. 생성자 인자가 많을 때는 Builder패턴 적용을 고려하자.
점층적 생성자 패턴(telescoping constructor pattern) : 객체에 설정하려는 속성들을 의미 별로 쪼개서, 점층적으로 여러 생성자를 준비하여 대응하는 패턴
public class NutritionFacts{
final int servingSize; // (mL) 필수
final int servings; // (per container) 필수
final int calories; // 선택
final int fat; // (g) 선택
final int sodium; // (mg) 선택
final int carbohydrate; // (g) 선택
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, servings, calories, fat, 0);
}
public NutritionFacts( int servingSize, int servings, int calories, int fat, int sodium ){
this(servingSize, servings, calories, fat, sodium, 0);
}
public NutritionFacts( int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate ){
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
}
점층적 생성자 패턴은 인자수가 늘어나면 클라이언트 코드를 작성하기가 어려워지고, 무엇 보다 읽기 어려운 코드가 된다.
setter를 사용하여, 설정 별로 쪼개서 생성자 호출을 피하는 방법도 있지만, 속성에 final 속성을 줄수 없으므로, 불변성(immutable)을 보장할 수 없다.
또한, 1회의 함수 호출로 객체 생성을 끝낼 수 없으므로, 객체 일관성(consistency)이 일시적으로 깨질 수 있다.
NutritionFacts
클래스가 불변함을 보장하고, 일관성 있게 객체의 속성을 설정하려면 아래와 같이 빌더 패턴을 검토 해보는 것이 좋다.
public class NutritionFacts{
final int servingSize; // (mL) 필수
final int servings; // (per container) 필수
final int calories; // 선택
final int fat; // (g) 선택
final int sodium; // (mg) 선택
final int carbohydrate; // (g) 선택
public static class Builder {
final int servingSize; // (mL) 필수
final int servings; // (per container) 필수
int calories; // 선택
int fat; // (g) 선택
int sodium; // (mg) 선택
int carbohydrate; // (g) 선택
public Builder( int servingSize, int servings ){
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories( int val ){
calories = val;
return this;
}
public Builder fat( int val ){
fat = val;
return this;
}
public Builder sodium( int val ){
sodium = val;
return this;
}
public Builder carbohydrate( int val ){
carbohydrate = val;
return this;
}
public NutritionFacts build(){
return new NutritionFacts( this );
}
}
private NutritionFacts( Builder builder ){
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
NutritionFacts
객체를 생성하기 위해서, Builder
라는 객체를 추가적으로 생성해야 하는 단점은 존재하지만, Python과 같은 언어에 있는 선택적 인자 설정과 비슷한 코드를 작성할 수 있다.
NutritionFacts cocaCola = new NutritionFacts
.Bilder(240, 8)
.calories(100)
.sodium(35)
.carbohydrate(27)
.build()
빌더 패턴은 인자가 많은 생성자나 정적 팩토리가 필요한 클래스를 설계할 때, 대부분의 인자가 선택적 인자인 상황에서 유용하다.